feat(monitor): show client risk to future on tickets
gitea/salix/pipeline/head This commit looks good Details

Refs: 2624
This commit is contained in:
Joan Sanchez 2021-10-15 10:49:23 +02:00
parent e278491ad2
commit 85b4730c6c
8 changed files with 110 additions and 52 deletions

View File

@ -130,54 +130,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};
} }
@ -217,6 +183,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,
@ -247,6 +214,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 = NULL
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
@ -260,17 +268,20 @@ module.exports = Self => {
stmts.push('CALL ticket_getProblems(FALSE)'); stmts.push('CALL ticket_getProblems(FALSE)');
stmt = new ParameterizedSQL(` stmt = new ParameterizedSQL(`
SELECT f.*, tp.* SELECT t.*, tp.*, t.debt + t.credit AS risk,
FROM tmp.filter f ((t.debt + t.credit) + 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': {gt: 0}},
{'tp.hasTicketRequest': true}, {'tp.hasTicketRequest': true},
@ -279,7 +290,7 @@ module.exports = Self => {
{'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},
@ -289,8 +300,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));
@ -299,7 +355,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

@ -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

@ -51,7 +51,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

@ -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'
}]); }]);