7182-workerDms_optimization #2946

Merged
alexm merged 7 commits from 7182-workerDms_optimization into dev 2024-09-12 07:28:08 +00:00
13 changed files with 219 additions and 48 deletions
Showing only changes of commit 17fecf6dcb - Show all commits

View File

@ -21,6 +21,7 @@ BEGIN
t.clientFk,
t.warehouseFk,
ts.alertLevel,
sub2.alertLevel futureAlertLevel,
t.shipped,
t.totalWithVat,
sub2.shipped futureShipped,
@ -47,6 +48,7 @@ BEGIN
t.addressFk,
t.id,
t.shipped,
ts.alertLevel,
st.name state,
st.code,
st.classColor,

View File

@ -0,0 +1,9 @@
-- Place your SQL code here
INSERT INTO salix.ACL
SET model = 'Ticket',
property = 'setWeight',
accessType = 'WRITE',
permission = 'ALLOW',
principalType = 'ROLE',
principalId = 'salesPerson';

View File

@ -687,8 +687,8 @@ export default {
ticketFuture: {
searchResult: 'vn-ticket-future tbody tr',
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
originDated: 'vn-date-picker[label="Origin date"]',
futureDated: 'vn-date-picker[label="Destination date"]',
originScopeDays: 'vn-date-picker[label="Origin date"]',
futureScopeDays: 'vn-date-picker[label="Destination date"]',
linesMax: 'vn-textfield[label="Max Lines"]',
litersMax: 'vn-textfield[label="Max Liters"]',
ipt: 'vn-autocomplete[label="Origin IPT"]',

View File

@ -30,18 +30,18 @@ describe('Ticket Future path', () => {
expect(message.text).toContain('warehouseFk is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.futureDated);
await page.clearInput(selectors.ticketFuture.futureScopeDays);
await page.waitToClick(selectors.ticketFuture.submit);
message = await page.waitForSnackbar();
expect(message.text).toContain('futureDated is a required argument');
expect(message.text).toContain('futureScopeDays is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.originDated);
await page.clearInput(selectors.ticketFuture.originScopeDays);
await page.waitToClick(selectors.ticketFuture.submit);
message = await page.waitForSnackbar();
expect(message.text).toContain('originDated is a required argument');
expect(message.text).toContain('originScopeDays is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
@ -71,7 +71,7 @@ describe('Ticket Future path', () => {
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('state=FREE');
expect(httpRequest).toContain('state=0');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
@ -80,7 +80,7 @@ describe('Ticket Future path', () => {
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureState=FREE');
expect(httpRequest).toContain('futureState=0');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state);

View File

@ -372,5 +372,7 @@
"The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada",
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe"
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe",
"Weight already set": "El peso ya está establecido",
"This ticket is not allocated to your department": "Este ticket no está asignado a tu departamento"
}

View File

@ -9,13 +9,13 @@ module.exports = Self => {
accessType: 'READ',
accepts: [
{
arg: 'originDated',
arg: 'originScopeDays',
type: 'date',
description: 'The date in question',
required: true
},
{
arg: 'futureDated',
arg: 'futureScopeDays',
type: 'date',
description: 'The date to probe',
required: true
@ -129,9 +129,9 @@ module.exports = Self => {
]
};
case 'state':
return {'f.stateCode': {like: `%${value}%`}};
return {'f.alertLevel': value};
case 'futureState':
return {'f.futureStateCode': {like: `%${value}%`}};
return {'f.futureAlertLevel': value};
}
});
@ -141,7 +141,7 @@ module.exports = Self => {
stmt = new ParameterizedSQL(
`CALL vn.ticket_canbePostponed(?,?,?)`,
[args.originDated, args.futureDated, args.warehouseFk]);
[args.originScopeDays, args.futureScopeDays, args.warehouseFk]);
stmts.push(stmt);
@ -170,7 +170,7 @@ module.exports = Self => {
LEFT JOIN tmp.ticket_problems tp ON tp.ticketFk = f.id
`);
if (args.problems != undefined && (!args.originDated && !args.futureDated))
if (args.problems != undefined && (!args.originScopeDays && !args.futureScopeDays))
throw new UserError('Choose a date range or days forward');
let condition;

View File

@ -0,0 +1,86 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('setWeight', {
description: 'Sets weight of a ticket',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The ticket id',
http: {source: 'path'}
}, {
arg: 'weight',
type: 'number',
required: true,
description: 'The weight value',
}],
returns: {
type: 'Array',
root: true
},
http: {
path: `/:id/setWeight`,
verb: 'POST'
}
});
Self.setWeight = async(ctx, ticketId, weight, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
let tx;
if (typeof options == 'object') Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const ticket = await Self.findById(ticketId, null, myOptions);
if (ticket.weight) throw new UserError('Weight already set');
const canEdit = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'updateAttributes');
const client = await models.Client.findById(ticket.clientFk, {
include: {relation: 'salesPersonUser'}},
myOptions);
if (!canEdit) {
const salesPersonUser = client.salesPersonUser();
const workerDepartments = await models.WorkerDepartment.find({
include: {relation: 'department'},
where: {workerFk: {inq: [userId, salesPersonUser.id]}}
}, myOptions);
if (
workerDepartments.length == 2 &&
workerDepartments[0].departmentFk != workerDepartments[1].departmentFk
)
throw new UserError('This ticket is not allocated to your department');
}
await ticket.updateAttribute('weight', weight, myOptions);
const packedState = await models.State.findOne({where: {code: 'PACKED'}}, myOptions);
const ticketState = await models.TicketState.findOne({where: {ticketFk: ticketId}}, myOptions);
const [{taxArea}] = await Self.rawSql('SELECT clientTaxArea(?,?) taxArea',
[ticket.clientFk, ticket.warehouseFk], myOptions);
const isInvoiceable = ticketState.alertLevel >= packedState.alertLevel &&
taxArea == 'WORLD' && client.hasDailyInvoice;
if (tx) await tx.commit();
let invoiceIds = [];
if (isInvoiceable) invoiceIds = [...await Self.invoiceTicketsAndPdf(ctx, [ticketId])];
return invoiceIds;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -12,8 +12,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
};
@ -35,8 +35,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
problems: true
};
@ -60,8 +60,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
problems: false
};
@ -85,8 +85,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
problems: null
};
@ -110,8 +110,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
state: 'OK'
};
@ -135,8 +135,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
futureState: 'OK'
};
@ -160,8 +160,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
ipt: null
};
@ -185,8 +185,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
ipt: 'H'
};
@ -210,8 +210,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
futureIpt: null
};
@ -235,8 +235,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
futureIpt: 'H'
};
@ -260,8 +260,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
id: 13
};
@ -285,8 +285,8 @@ describe('ticket getTicketsFuture()', () => {
const options = {transaction: tx};
const args = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: 1,
futureId: 12
};

View File

@ -0,0 +1,70 @@
const {models} = require('vn-loopback/server/server');
describe('ticket setWeight()', () => {
const ctx = beforeAll.getCtx();
beforeAll.mockLoopBackContext();
let opts;
let tx;
const employeeId = 1;
const administrativeId = 5;
beforeEach(async() => {
opts = {transaction: tx};
tx = await models.Ticket.beginTransaction({});
opts.transaction = tx;
ctx.req.accessToken.userId = administrativeId;
});
afterEach(async() => await tx.rollback());
it('should throw an error if the weight is already set', async() => {
try {
const ticketId = 1;
const weight = 10;
await models.Ticket.setWeight(ctx, ticketId, weight, opts);
} catch (e) {
expect(e.message).toEqual('Weight already set');
}
});
it('should set the weight of a ticket', async() => {
const ticketId = 31;
const weight = 15;
await models.Ticket.setWeight(ctx, ticketId, weight, opts);
const ticket = await models.Ticket.findById(ticketId, null, opts);
expect(ticket.weight).toEqual(weight);
});
it('should throw an error if the user is not allocated to the same department', async() => {
ctx.req.accessToken.userId = employeeId;
try {
const ticketId = 10;
const weight = 20;
await models.Ticket.setWeight(ctx, ticketId, weight, opts);
} catch (e) {
expect(e.message).toEqual('This ticket is not allocated to your department');
}
});
it('should call invoiceTicketsAndPdf if the ticket is invoiceable', async() => {
const ticketId = 10;
const weight = 25;
spyOn(models.Client, 'findById').and.returnValue({
hasDailyInvoice: true,
salesPersonUser: () => ({id: 1})
});
spyOn(models.TicketState, 'findOne').and.returnValue({alertLevel: 3});
spyOn(models.Ticket, 'rawSql').and.returnValue([{taxArea: 'WORLD'}]);
spyOn(models.Ticket, 'invoiceTicketsAndPdf').and.returnValue([10]);
const invoiceIds = await models.Ticket.setWeight(ctx, ticketId, weight, opts);
expect(invoiceIds.length).toBeGreaterThan(0);
});
});

View File

@ -46,4 +46,5 @@ module.exports = function(Self) {
require('../methods/ticket/invoiceTicketsAndPdf')(Self);
require('../methods/ticket/docuwareDownload')(Self);
require('../methods/ticket/myLastModified')(Self);
require('../methods/ticket/setWeight')(Self);
};

View File

@ -9,13 +9,13 @@
<vn-date-picker
vn-one
label="Origin date"
ng-model="filter.originDated"
ng-model="filter.originScopeDays"
required="true">
</vn-date-picker>
<vn-date-picker
vn-one
label="Destination date"
ng-model="filter.futureDated"
ng-model="filter.futureScopeDays"
required="true">
</vn-date-picker>
</vn-horizontal>
@ -59,7 +59,7 @@
<vn-autocomplete vn-one
data="$ctrl.groupedStates"
label="Origin Grouped State"
value-field="code"
value-field="id"
show-field="name"
ng-model="filter.state">
<tpl-item>
@ -69,7 +69,7 @@
<vn-autocomplete vn-one
data="$ctrl.groupedStates"
label="Destination Grouped State"
value-field="code"
value-field="id"
show-field="name"
ng-model="filter.futureState">
<tpl-item>

View File

@ -65,8 +65,8 @@ export default class Controller extends Section {
this.$http.get(`UserConfigs/getUserConfig`)
.then(res => {
this.filterParams = {
originDated: today,
futureDated: today,
originScopeDays: today,
futureScopeDays: today,
warehouseFk: res.data.warehouseFk
};
this.$.model.applyFilter(null, this.filterParams);

View File

@ -4,3 +4,4 @@ Client phone: Tel. cliente
Client mobile: Móv. cliente
Go to the ticket: Ir al ticket
Change state: Cambiar estado
Weight: Peso