Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 3092-module_transactions
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2021-10-15 14:47:34 +02:00
commit 05df98939b
15 changed files with 194 additions and 69 deletions

View File

@ -0,0 +1,48 @@
drop procedure `vn`.`ticket_getProblems`;
DELIMITER $$
$$
create
definer = root@`%` procedure `vn`.`ticket_getProblems`(IN vIsTodayRelative tinyint(1))
BEGIN
/**
* Calcula los problemas para un conjunto de tickets.
* Agrupados por ticket
*
* @table tmp.sale_getProblems(ticketFk, clientFk, warehouseFk, shipped) Identificadores de los tickets a calcular
* @return tmp.ticket_problems
*/
CALL sale_getProblems(vIsTodayRelative);
DROP TEMPORARY TABLE IF EXISTS tmp.ticket_problems;
CREATE TEMPORARY TABLE tmp.ticket_problems
(PRIMARY KEY (ticketFk))
ENGINE = MEMORY
SELECT
ticketFk,
MAX(p.isFreezed) AS isFreezed,
MAX(p.risk) AS risk,
MAX(p.hasHighRisk) AS hasHighRisk,
MAX(p.hasTicketRequest) AS hasTicketRequest,
MIN(p.isAvailable) AS isAvailable,
MAX(p.itemShortage) AS itemShortage,
MIN(p.isTaxDataChecked) AS isTaxDataChecked,
MAX(p.hasComponentLack) AS hasComponentLack,
0 AS totalProblems
FROM tmp.sale_problems p
GROUP BY ticketFk;
UPDATE tmp.ticket_problems tp
SET tp.totalProblems = (
(tp.isFreezed) +
IF(tp.risk, TRUE, FALSE) +
(tp.hasTicketRequest) +
(tp.isAvailable = 0) +
(tp.isTaxDataChecked = 0) +
(tp.hasComponentLack)
);
DROP TEMPORARY TABLE
tmp.sale_problems;
END;;$$
DELIMITER ;

View File

@ -1,9 +1,10 @@
<div ng-if="$ctrl.model.moreRows"> <div ng-if="$ctrl.model.moreRows" class="vn-py-md">
<vn-button <div
ng-if="!$ctrl.model.isPaging" ng-if="!$ctrl.model.isPaging"
label="Load more results"
ng-click="$ctrl.onLoadClick()"> ng-click="$ctrl.onLoadClick()">
</vn-button> <vn-button label="Load more results"></vn-button>
<vn-icon icon="arrow_drop_down"/>
</div>
<vn-spinner <vn-spinner
ng-if="$ctrl.model.isPaging" ng-if="$ctrl.model.isPaging"
enable="::true"> enable="::true">

View File

@ -1,7 +1,13 @@
@import "variables";
vn-pagination { vn-pagination {
display: block; display: block;
text-align: center; text-align: center;
color: $color-primary;
vn-button, vn-icon {
display: block
}
& > div > vn-icon-button { & > div > vn-icon-button {
font-size: 2rem; font-size: 2rem;

View File

@ -129,54 +129,20 @@ module.exports = Self => {
const where = buildFilter(ctx.args, (param, value) => { const where = buildFilter(ctx.args, (param, value) => {
switch (param) { switch (param) {
case 'search':
return /^\d+$/.test(value)
? {'t.id': {inq: value}}
: {'t.nickname': {like: `%${value}%`}};
case 'from': case 'from':
return {'t.shipped': {gte: value}}; return {'t.shipped': {gte: value}};
case 'to': case 'to':
return {'t.shipped': {lte: value}}; return {'t.shipped': {lte: value}};
case 'nickname':
return {'t.nickname': {like: `%${value}%`}};
case 'refFk':
return {'t.refFk': value};
case 'salesPersonFk': case 'salesPersonFk':
return {'c.salesPersonFk': value}; return {'c.salesPersonFk': value};
case 'provinceFk':
return {'a.provinceFk': value};
case 'stateFk':
return {'ts.stateFk': value};
case 'mine': case 'mine':
case 'myTeam': case 'myTeam':
if (value) if (value)
return {'c.salesPersonFk': {inq: teamMembersId}}; return {'c.salesPersonFk': {inq: teamMembersId}};
else else
return {'c.salesPersonFk': {nin: teamMembersId}}; return {'c.salesPersonFk': {nin: teamMembersId}};
case 'alertLevel':
return {'ts.alertLevel': value};
case 'pending':
if (value) {
return {and: [
{'st.alertLevel': 0},
{'st.code': {nin: [
'OK',
'BOARDING',
'PRINTED',
'PRINTED_AUTO',
'PICKER_DESIGNED'
]}}
]};
} else {
return {and: [
{'st.alertLevel': {gt: 0}}
]};
}
case 'id': case 'id':
case 'clientFk': case 'clientFk':
case 'agencyModeFk':
case 'warehouseFk':
param = `t.${param}`; param = `t.${param}`;
return {[param]: value}; return {[param]: value};
} }
@ -216,6 +182,7 @@ module.exports = Self => {
ts.code AS alertLevelCode, ts.code AS alertLevelCode,
u.name AS userName, u.name AS userName,
c.salesPersonFk, c.salesPersonFk,
c.credit,
z.hour AS zoneLanding, z.hour AS zoneLanding,
z.name AS zoneName, z.name AS zoneName,
z.id AS zoneFk, z.id AS zoneFk,
@ -246,6 +213,47 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
stmts.push(stmt); stmts.push(stmt);
// Get client debt balance
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.clientGetDebt');
stmts.push(`
CREATE TEMPORARY TABLE tmp.clientGetDebt
(INDEX (clientFk))
ENGINE = MEMORY
SELECT DISTINCT clientFk FROM tmp.filter`);
stmt = new ParameterizedSQL('CALL clientGetDebt(?)', [args.to]);
stmts.push(stmt);
stmts.push('DROP TEMPORARY TABLE tmp.clientGetDebt');
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.tickets');
stmt = new ParameterizedSQL(`
CREATE TEMPORARY TABLE tmp.tickets
(INDEX (id))
ENGINE = MEMORY
SELECT f.*, r.risk AS debt
FROM tmp.filter f
LEFT JOIN tmp.risk r ON f.clientFk = r.clientFk`);
stmts.push(stmt);
// Sum risk to future
stmts.push(`SET @client:= 0`);
stmts.push('SET @risk := 0');
stmts.push(`
UPDATE tmp.tickets
SET debt = IF(@client <> @client:= clientFk,
-totalWithVat + @risk:= - debt + totalWithVat,
-totalWithVat + @risk:= @risk + totalWithVat
)
ORDER BY clientFk, shipped DESC
`);
// Remove positive risks
stmts.push(`
UPDATE tmp.tickets t
SET debt = 0
WHERE t.debt + t.credit >= 0
`);
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems'); stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
stmts.push(` stmts.push(`
CREATE TEMPORARY TABLE tmp.sale_getProblems CREATE TEMPORARY TABLE tmp.sale_getProblems
@ -258,27 +266,39 @@ module.exports = Self => {
AND f.shipped >= CURDATE()`); AND f.shipped >= CURDATE()`);
stmts.push('CALL ticket_getProblems(FALSE)'); stmts.push('CALL ticket_getProblems(FALSE)');
stmts.push(`
INSERT INTO tmp.ticket_problems (ticketFk, risk, totalProblems)
SELECT t.id, t.debt + t.credit AS risk, 1
FROM tmp.tickets t
WHERE (t.debt + t.credit) < 0
ON DUPLICATE KEY UPDATE
risk = t.debt + t.credit, totalProblems = totalProblems + 1
`);
stmt = new ParameterizedSQL(` stmt = new ParameterizedSQL(`
SELECT f.*, tp.* SELECT t.*, tp.*,
FROM tmp.filter f ((tp.risk) + cc.riskTolerance < 0) AS hasHighRisk
LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id`); FROM tmp.tickets t
LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = t.id
JOIN clientConfig cc`);
const hasProblems = args.problems; const hasProblems = args.problems;
if (hasProblems != undefined && (!args.from && !args.to)) if (hasProblems != undefined && (!args.from && !args.to))
throw new UserError('Choose a date range or days forward'); throw new UserError('Choose a date range or days forward');
let problemsFilter; let finalFilter = {};
let whereProblems;
if (hasProblems === true) { if (hasProblems === true) {
problemsFilter = {or: [ whereProblems = {or: [
{'tp.isFreezed': true}, {'tp.isFreezed': true},
{'tp.risk': {gt: 0}}, {'tp.risk': {lt: 0}},
{'tp.hasTicketRequest': true}, {'tp.hasTicketRequest': true},
{'tp.hasComponentLack': true}, {'tp.hasComponentLack': true},
{'tp.isTaxDataChecked': false}, {'tp.isTaxDataChecked': false},
{'tp.isAvailable': false} {'tp.isAvailable': false}
]}; ]};
} else if (hasProblems === false) { } else if (hasProblems === false) {
problemsFilter = {and: [ whereProblems = {and: [
{'tp.isFreezed': false}, {'tp.isFreezed': false},
{'tp.risk': 0}, {'tp.risk': 0},
{'tp.hasTicketRequest': false}, {'tp.hasTicketRequest': false},
@ -288,8 +308,53 @@ module.exports = Self => {
]}; ]};
} }
if (problemsFilter) if (whereProblems) finalFilter.where = whereProblems;
stmt.merge(conn.makeWhere(problemsFilter));
const myWhere = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {'t.id': {inq: value}}
: {'t.nickname': {like: `%${value}%`}};
case 'nickname':
return {'t.nickname': {like: `%${value}%`}};
case 'refFk':
return {'t.refFk': value};
case 'provinceFk':
return {'t.provinceFk': value};
case 'stateFk':
return {'t.stateFk': value};
case 'alertLevel':
return {'t.alertLevel': value};
case 'pending':
if (value) {
return {and: [
{'t.alertLevel': 0},
{'t.alertLevelCode': {nin: [
'OK',
'BOARDING',
'PRINTED',
'PRINTED_AUTO',
'PICKER_DESIGNED'
]}}
]};
} else {
return {and: [
{'t.alertLevel': {gt: 0}}
]};
}
case 'agencyModeFk':
case 'warehouseFk':
param = `t.${param}`;
return {[param]: value};
}
});
finalFilter = mergeFilters(finalFilter, {where: myWhere});
if (finalFilter.where)
stmt.merge(conn.makeWhere(finalFilter.where));
stmt.merge(conn.makeOrderBy(filter.order)); stmt.merge(conn.makeOrderBy(filter.order));
stmt.merge(conn.makeLimit(filter)); stmt.merge(conn.makeLimit(filter));
@ -298,7 +363,9 @@ module.exports = Self => {
stmts.push( stmts.push(
`DROP TEMPORARY TABLE `DROP TEMPORARY TABLE
tmp.filter, tmp.filter,
tmp.ticket_problems`); tmp.ticket_problems,
tmp.sale_getProblems,
tmp.risk`);
let sql = ParameterizedSQL.join(stmts, ';'); let sql = ParameterizedSQL.join(stmts, ';');
let result = await conn.executeStmt(sql); let result = await conn.executeStmt(sql);

View File

@ -23,7 +23,7 @@ describe('SalesMonitor salesFilter()', () => {
const filter = {}; const filter = {};
const result = await models.SalesMonitor.salesFilter(ctx, filter); const result = await models.SalesMonitor.salesFilter(ctx, filter);
expect(result.length).toEqual(9); expect(result.length).toEqual(13);
}); });
it('should now return the tickets matching the problems on false', async() => { it('should now return the tickets matching the problems on false', async() => {

View File

@ -70,7 +70,7 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
data="$ctrl.groupedStates" data="$ctrl.groupedStates"
label="Grouped States" label="Grouped States"
value-field="alertLevel" value-field="id"
show-field="name" show-field="name"
ng-model="filter.alertLevel"> ng-model="filter.alertLevel">
<tpl-item> <tpl-item>

View File

@ -14,7 +14,7 @@ class Controller extends SearchPanel {
this.$http.get('AlertLevels').then(res => { this.$http.get('AlertLevels').then(res => {
for (let state of res.data) { for (let state of res.data) {
groupedStates.push({ groupedStates.push({
alertLevel: state.alertLevel, id: state.id,
code: state.code, code: state.code,
name: this.$t(state.code) name: this.$t(state.code)
}); });

View File

@ -18,7 +18,7 @@ describe('Monitor Component vnMonitorSalesSearchPanel', () => {
jest.spyOn(controller, '$t').mockReturnValue('miCodigo'); jest.spyOn(controller, '$t').mockReturnValue('miCodigo');
const data = [ const data = [
{ {
alertLevel: 9999, id: 9999,
code: 'myCode' code: 'myCode'
} }
]; ];
@ -27,7 +27,7 @@ describe('Monitor Component vnMonitorSalesSearchPanel', () => {
$httpBackend.flush(); $httpBackend.flush();
expect(controller.groupedStates).toEqual([{ expect(controller.groupedStates).toEqual([{
alertLevel: 9999, id: 9999,
code: 'myCode', code: 'myCode',
name: 'miCodigo' name: 'miCodigo'
}]); }]);

View File

@ -50,7 +50,7 @@
<a ng-repeat="ticket in model.data" <a ng-repeat="ticket in model.data"
class="clickable vn-tr search-result" class="clickable vn-tr search-result"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})" target="_blank"> ui-sref="ticket.card.summary({id: {{::ticket.id}}})" target="_blank">
<vn-td class="icon-field"> <vn-td expand>
<vn-icon <vn-icon
ng-show="::ticket.isTaxDataChecked === 0" ng-show="::ticket.isTaxDataChecked === 0"
translate-attr="{title: 'No verified data'}" translate-attr="{title: 'No verified data'}"

View File

@ -119,7 +119,7 @@ module.exports = Self => {
case 'sourceApp': case 'sourceApp':
return {'o.source_app': value}; return {'o.source_app': value};
case 'ticketFk': case 'ticketFk':
return {'ort.ticketFk': value}; return {'ot.ticketFk': value};
case 'isConfirmed': case 'isConfirmed':
return {'o.confirmed': value ? 1 : 0}; return {'o.confirmed': value ? 1 : 0};
case 'myTeam': case 'myTeam':
@ -137,7 +137,10 @@ module.exports = Self => {
let stmt; let stmt;
stmt = new ParameterizedSQL( stmt = new ParameterizedSQL(
`SELECT `CREATE TEMPORARY TABLE tmp.filter
(INDEX (id))
ENGINE = MEMORY
SELECT
o.id, o.id,
o.total, o.total,
o.date_send landed, o.date_send landed,
@ -168,20 +171,20 @@ module.exports = Self => {
LEFT JOIN ticket t ON t.id = ot.ticketFk LEFT JOIN ticket t ON t.id = ot.ticketFk
LEFT JOIN zoneEstimatedDelivery zed ON zed.zoneFk = t.zoneFk`); LEFT JOIN zoneEstimatedDelivery zed ON zed.zoneFk = t.zoneFk`);
if (args && args.ticketFk) {
stmt.merge({
sql: `LEFT JOIN orderTicket ort ON ort.orderFk = o.id`
});
}
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
stmt.merge(`GROUP BY o.id`);
stmt.merge(conn.makePagination(filter)); stmt.merge(conn.makePagination(filter));
stmts.push(stmt); stmts.push(stmt);
stmt = new ParameterizedSQL(`SELECT * FROM tmp.filter`);
stmt.merge(`GROUP BY id`);
stmt.merge(conn.makeOrderBy(filter.order));
const ordersIndex = stmts.push(stmt) - 1;
stmts.push(`DROP TEMPORARY TABLE tmp.filter`);
const sql = ParameterizedSQL.join(stmts, ';'); const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql); const result = await conn.executeStmt(sql);
return result; return result[ordersIndex];
}; };
}; };

View File

@ -17,7 +17,7 @@
<vn-th field="landed" shrink-date>Landed</vn-th> <vn-th field="landed" shrink-date>Landed</vn-th>
<vn-th field="created" center>Hour</vn-th> <vn-th field="created" center>Hour</vn-th>
<vn-th field="agencyName" center>Agency</vn-th> <vn-th field="agencyName" center>Agency</vn-th>
<vn-th center>Total</vn-th> <vn-th field="total" center>Total</vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<vn-tbody> <vn-tbody>

View File

@ -89,7 +89,7 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
data="$ctrl.groupedStates" data="$ctrl.groupedStates"
label="Grouped States" label="Grouped States"
value-field="alertLevel" value-field="id"
show-field="name" show-field="name"
ng-model="filter.alertLevel"> ng-model="filter.alertLevel">
<tpl-item> <tpl-item>

View File

@ -14,7 +14,7 @@ class Controller extends SearchPanel {
this.$http.get('AlertLevels').then(res => { this.$http.get('AlertLevels').then(res => {
for (let state of res.data) { for (let state of res.data) {
groupedStates.push({ groupedStates.push({
alertLevel: state.alertLevel, id: state.id,
code: state.code, code: state.code,
name: this.$t(state.code) name: this.$t(state.code)
}); });

View File

@ -18,7 +18,7 @@ describe('Ticket Component vnTicketSearchPanel', () => {
jest.spyOn(controller, '$t').mockReturnValue('miCodigo'); jest.spyOn(controller, '$t').mockReturnValue('miCodigo');
const data = [ const data = [
{ {
alertLevel: 9999, id: 9999,
code: 'myCode' code: 'myCode'
} }
]; ];
@ -27,7 +27,7 @@ describe('Ticket Component vnTicketSearchPanel', () => {
$httpBackend.flush(); $httpBackend.flush();
expect(controller.groupedStates).toEqual([{ expect(controller.groupedStates).toEqual([{
alertLevel: 9999, id: 9999,
code: 'myCode', code: 'myCode',
name: 'miCodigo' name: 'miCodigo'
}]); }]);