Merge pull request '2896-route_tickets_refactor + transactions + unitest + e2e' (#623) from 2896-route_tickets_refactor into dev
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
Reviewed-on: #623 Reviewed-by: Joan Sanchez <joan@verdnatura.es>
This commit is contained in:
commit
ea1ff3e5fa
|
@ -24,11 +24,16 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
||||
Self.sendCheckingPresence = async(ctx, recipientId, message, options) => {
|
||||
if (!recipientId) return false;
|
||||
|
||||
let myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const models = Self.app.models;
|
||||
const account = await models.Account.findById(recipientId);
|
||||
const account = await models.Account.findById(recipientId, null, myOptions);
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
if (recipientId == userId) return false;
|
||||
|
@ -37,14 +42,14 @@ module.exports = Self => {
|
|||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||
|
||||
const query = `SELECT worker_isWorking(?) isWorking`;
|
||||
const [result] = await Self.rawSql(query, [recipientId]);
|
||||
const [result] = await Self.rawSql(query, [recipientId], myOptions);
|
||||
|
||||
if (!result.isWorking) {
|
||||
const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
|
||||
include: {
|
||||
relation: 'department'
|
||||
}
|
||||
});
|
||||
}, myOptions);
|
||||
const department = workerDepartment && workerDepartment.department();
|
||||
const channelName = department && department.chatName;
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Delete me
|
|
@ -0,0 +1,92 @@
|
|||
DROP PROCEDURE `vn`.`ticket_componentMakeUpdate`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE
|
||||
DEFINER = root@`%` PROCEDURE `vn`.`ticket_componentMakeUpdate`(IN vTicketFk INT, IN vClientFk INT, IN vAgencyModeFk INT,
|
||||
IN vAddressFk INT, IN vZoneFk INT, IN vWarehouseFk TINYINT,
|
||||
IN vCompanyFk SMALLINT, IN vShipped DATETIME,
|
||||
IN vLanded DATE, IN vIsDeleted TINYINT(1),
|
||||
IN vHasToBeUnrouted TINYINT(1), IN vOption INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Modifica en el ticket los campos que se le pasan por parámetro
|
||||
* y cambia sus componentes.
|
||||
* Este procedimiento es transacionado en Salix
|
||||
*
|
||||
* @param vTicketFk Id del ticket a modificar
|
||||
* @param vClientFk nuevo cliente
|
||||
* @param vAgencyModeFk nueva agencia
|
||||
* @param vAddressFk nuevo consignatario
|
||||
* @param vZoneFk nueva zona
|
||||
* @param vWarehouseFk nuevo almacen
|
||||
* @param vCompanyFk nueva empresa
|
||||
* @param vShipped nueva fecha del envio de mercancia
|
||||
* @param vLanded nueva fecha de recepcion de mercancia
|
||||
* @param vIsDeleted si se borra el ticket
|
||||
* @param vHasToBeUnrouted si se le elimina la ruta al ticket
|
||||
* @param vOption opcion para el case del proc ticketComponentUpdateSale
|
||||
*/
|
||||
DECLARE vPrice DECIMAL(10,2);
|
||||
DECLARE vBonus DECIMAL(10,2);
|
||||
|
||||
CALL ticket_componentPreview (vTicketFk, vLanded, vAddressFk, vZoneFk, vWarehouseFk);
|
||||
|
||||
IF (SELECT addressFk FROM ticket WHERE id = vTicketFk) <> vAddressFk THEN
|
||||
|
||||
UPDATE ticket t
|
||||
JOIN address a ON a.id = vAddressFk
|
||||
SET t.nickname = a.nickname
|
||||
WHERE t.id = vTicketFk;
|
||||
|
||||
END IF;
|
||||
|
||||
CALL zone_getShippedWarehouse(vlanded, vAddressFk, vAgencyModeFk);
|
||||
|
||||
SELECT zoneFk, price, bonus INTO vZoneFk, vPrice, vBonus
|
||||
FROM tmp.zoneGetShipped
|
||||
WHERE shipped BETWEEN DATE(vShipped) AND util.dayEnd(vShipped) AND warehouseFk = vWarehouseFk LIMIT 1;
|
||||
|
||||
UPDATE ticket t
|
||||
SET
|
||||
t.clientFk = vClientFk,
|
||||
t.agencyModeFk = vAgencyModeFk,
|
||||
t.addressFk = vAddressFk,
|
||||
t.zoneFk = vZoneFk,
|
||||
t.zonePrice = vPrice,
|
||||
t.zoneBonus = vBonus,
|
||||
t.warehouseFk = vWarehouseFk,
|
||||
t.companyFk = vCompanyFk,
|
||||
t.landed = vLanded,
|
||||
t.shipped = vShipped,
|
||||
t.isDeleted = vIsDeleted
|
||||
WHERE
|
||||
t.id = vTicketFk;
|
||||
|
||||
IF vHasToBeUnrouted THEN
|
||||
UPDATE ticket t SET t.routeFk = NULL
|
||||
WHERE t.id = vTicketFk;
|
||||
END IF;
|
||||
|
||||
IF vOption <> 8 THEN
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
|
||||
CREATE TEMPORARY TABLE tmp.sale
|
||||
(PRIMARY KEY (saleFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT id AS saleFk, vWarehouseFk warehouseFk
|
||||
FROM sale s WHERE s.ticketFk = vTicketFk;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
|
||||
CREATE TEMPORARY TABLE tmp.ticketComponent
|
||||
SELECT * FROM tmp.ticketComponentPreview;
|
||||
|
||||
CALL ticketComponentUpdateSale (vOption);
|
||||
|
||||
DROP TEMPORARY TABLE tmp.sale;
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
|
||||
END IF;
|
||||
|
||||
DROP TEMPORARY TABLE tmp.zoneGetShipped, tmp.ticketComponentPreview;
|
||||
END;;$$
|
||||
DELIMITER ;
|
||||
|
|
@ -410,11 +410,12 @@ INSERT INTO `vn`.`clientObservation`(`id`, `clientFk`, `workerFk`, `text`, `crea
|
|||
|
||||
INSERT INTO `vn`.`observationType`(`id`,`description`, `code`)
|
||||
VALUES
|
||||
(1, 'observation one', 'observation one'),
|
||||
(2, 'observation two', 'observation two'),
|
||||
(3, 'observation three', 'observation three'),
|
||||
(4, 'comercial', 'salesPerson'),
|
||||
(5, 'delivery', 'delivery');
|
||||
(1, 'ItemPicker', 'itemPicker'),
|
||||
(2, 'Packager', 'packager'),
|
||||
(3, 'Delivery', 'delivery'),
|
||||
(4, 'SalesPerson', 'salesPerson'),
|
||||
(5, 'Administrative', 'administrative'),
|
||||
(6, 'Weight', 'weight');
|
||||
|
||||
INSERT INTO `vn`.`addressObservation`(`id`,`addressFk`,`observationTypeFk`,`description`)
|
||||
VALUES
|
||||
|
@ -607,16 +608,16 @@ INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `des
|
|||
VALUES
|
||||
(1, 11, 1, 'ready'),
|
||||
(2, 2, 2, 'do it fast please'),
|
||||
(3, 3, 3, 'Faster faster fasteeeeeer!!!'),
|
||||
(4, 4, 3, 'Deliver before 8am'),
|
||||
(5, 13, 3, 'You can run from the disappointments you are trying to forget. But its only when you embrace your past that you truly move forward. Maybe I never get to go home again, but I found my way there. And I am glad I did.'),
|
||||
(6, 14, 3, 'Careful, armed warhead'),
|
||||
(3, 3, 5, 'Faster faster fasteeeeeer!!!'),
|
||||
(4, 4, 5, 'Deliver before 8am'),
|
||||
(5, 13, 5, 'You can run from the disappointments you are trying to forget. But its only when you embrace your past that you truly move forward. Maybe I never get to go home again, but I found my way there. And I am glad I did.'),
|
||||
(6, 14, 5, 'Careful, armed warhead'),
|
||||
(7, 23, 1, 'under the floor'),
|
||||
(8, 23, 2, 'wears leather and goes out at night'),
|
||||
(9, 23, 3, 'care with the dog'),
|
||||
(9, 23, 5, 'care with the dog'),
|
||||
(10, 23, 4, 'Reclama ticket: 8'),
|
||||
(11, 24, 4, 'Reclama ticket: 7'),
|
||||
(12, 11, 5, 'Delivery after 10am');
|
||||
(12, 11, 3, 'Delivery after 10am');
|
||||
|
||||
-- FIX for state hours on local, inter_afterInsert
|
||||
UPDATE vncontrol.inter SET odbc_date = DATE_ADD(CURDATE(), INTERVAL -10 SECOND);
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('Client add address notes path', () => {
|
|||
|
||||
it('should not save an observation type without description', async() => {
|
||||
await page.clearInput(selectors.clientAddresses.firstObservationDescription);
|
||||
await page.autocompleteSearch(selectors.clientAddresses.firstObservationType, 'comercial');
|
||||
await page.autocompleteSearch(selectors.clientAddresses.firstObservationType, 'SalesPerson');
|
||||
await page.waitToClick(selectors.clientAddresses.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
|
@ -43,7 +43,7 @@ describe('Client add address notes path', () => {
|
|||
it('should create two new observations', async() => {
|
||||
await page.write(selectors.clientAddresses.firstObservationDescription, 'first description');
|
||||
await page.waitToClick(selectors.clientAddresses.addObservationButton);
|
||||
await page.autocompleteSearch(selectors.clientAddresses.secondObservationType, 'observation one');
|
||||
await page.autocompleteSearch(selectors.clientAddresses.secondObservationType, 'ItemPicker');
|
||||
await page.write(selectors.clientAddresses.secondObservationDescription, 'second description');
|
||||
await page.waitToClick(selectors.clientAddresses.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('Ticket Create notes path', () => {
|
|||
|
||||
it('should create a new note', async() => {
|
||||
await page.waitToClick(selectors.ticketNotes.addNoteButton);
|
||||
await page.autocompleteSearch(selectors.ticketNotes.firstNoteType, 'observation one');
|
||||
await page.autocompleteSearch(selectors.ticketNotes.firstNoteType, 'ItemPicker');
|
||||
await page.write(selectors.ticketNotes.firstDescription, 'description');
|
||||
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
@ -32,7 +32,7 @@ describe('Ticket Create notes path', () => {
|
|||
const result = await page
|
||||
.waitToGetProperty(selectors.ticketNotes.firstNoteType, 'value');
|
||||
|
||||
expect(result).toEqual('observation one');
|
||||
expect(result).toEqual('ItemPicker');
|
||||
|
||||
const firstDescription = await page
|
||||
.waitToGetProperty(selectors.ticketNotes.firstDescription, 'value');
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('Ticket log path', () => {
|
|||
|
||||
it('should create a new note for the test', async() => {
|
||||
await page.waitToClick(selectors.ticketNotes.addNoteButton);
|
||||
await page.autocompleteSearch(selectors.ticketNotes.firstNoteType, 'observation one');
|
||||
await page.autocompleteSearch(selectors.ticketNotes.firstNoteType, 'ItemPicker');
|
||||
await page.write(selectors.ticketNotes.firstDescription, 'description');
|
||||
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
|
|
@ -17,7 +17,6 @@ describe('Entry lastest buys path', () => {
|
|||
|
||||
it('should access the latest buys seccion and search not seeing the edit buys button yet', async() => {
|
||||
await page.waitToClick(selectors.entryLatestBuys.latestBuysSectionButton);
|
||||
await page.waitToClick(selectors.globalItems.searchButton);
|
||||
await page.waitForSelector(selectors.entryLatestBuys.editBuysButton, {visible: false});
|
||||
});
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ describe('Entry observations path', () => {
|
|||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
// await page.loginAndModule('buyer', 'entry'); // access denied, awaiting role confirmation
|
||||
await page.loginAndModule('developer', 'entry');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.accessToSection('entry.card.observation');
|
||||
|
@ -21,8 +20,8 @@ describe('Entry observations path', () => {
|
|||
it(`should add two new observations of the same type then fail to save as they can't be repeated`, async() => {
|
||||
await page.waitToClick(selectors.entryObservations.addNewObservation);
|
||||
await page.waitToClick(selectors.entryObservations.addNewObservation);
|
||||
await page.autocompleteSearch(selectors.entryObservations.firstObservationType, 'comercial');
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'comercial');
|
||||
await page.autocompleteSearch(selectors.entryObservations.firstObservationType, 'SalesPerson');
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'SalesPerson');
|
||||
await page.write(selectors.entryObservations.firstObservationDescription, 'first observation');
|
||||
await page.write(selectors.entryObservations.secondObservationDescription, 'second observation');
|
||||
await page.waitToClick(selectors.entryObservations.saveObservationsButton);
|
||||
|
@ -32,7 +31,7 @@ describe('Entry observations path', () => {
|
|||
});
|
||||
|
||||
it('should set the 2nd observation of a different one and successfully save both', async() => {
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'delivery');
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'Delivery');
|
||||
await page.waitToClick(selectors.entryObservations.saveObservationsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
|
@ -43,7 +42,7 @@ describe('Entry observations path', () => {
|
|||
await page.reloadSection('entry.card.observation');
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.firstObservationType, 'value');
|
||||
|
||||
expect(result).toEqual('comercial');
|
||||
expect(result).toEqual('SalesPerson');
|
||||
});
|
||||
|
||||
it('should make sure the first observation description was saved correctly', async() => {
|
||||
|
@ -55,7 +54,7 @@ describe('Entry observations path', () => {
|
|||
it('should make sure the second observation type was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.secondObservationType, 'value');
|
||||
|
||||
expect(result).toEqual('delivery');
|
||||
expect(result).toEqual('Delivery');
|
||||
});
|
||||
|
||||
it('should make sure the second observation description was saved correctly', async() => {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Translates to a readable values
|
||||
* @param {Object} instance - The model or context instance
|
||||
* @param {Object} changes - Object containing changes
|
||||
* @param {Object} options - Object containing transaction
|
||||
*/
|
||||
exports.translateValues = async(instance, changes, options = {}) => {
|
||||
const models = instance.app.models;
|
||||
|
|
|
@ -79,7 +79,7 @@ module.exports = Self => {
|
|||
}, myOptions);
|
||||
|
||||
const obsevationType = await models.ObservationType.findOne({
|
||||
where: {description: 'comercial'}
|
||||
where: {code: 'salesPerson'}
|
||||
}, myOptions);
|
||||
|
||||
const agencyMode = await models.AgencyMode.findOne({
|
||||
|
|
|
@ -10,58 +10,67 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'search',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'Searchs the route by id',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'workerFk',
|
||||
type: 'Integer',
|
||||
type: 'integer',
|
||||
description: 'The worker id',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'agencyModeFk',
|
||||
type: 'Integer',
|
||||
type: 'integer',
|
||||
description: 'The agencyMode id',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'to',
|
||||
type: 'Date',
|
||||
type: 'date',
|
||||
description: 'The to date filter',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'from',
|
||||
type: 'Date',
|
||||
type: 'date',
|
||||
description: 'The to date filter',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'vehicleFk',
|
||||
type: 'Integer',
|
||||
type: 'integer',
|
||||
description: 'The vehicle id',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'm3',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The m3 filter',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'warehouseFk',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The warehouse filter',
|
||||
http: {source: 'query'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'description',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The description filter',
|
||||
http: {source: 'query'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
|
|
@ -1,101 +1,77 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('getTickets', {
|
||||
description: 'Return the tickets information displayed on the route module',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The route id',
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/getTickets`,
|
||||
path: `/getTickets`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getTickets = async id => {
|
||||
let filter = {
|
||||
where: {id: id},
|
||||
include: [
|
||||
{relation: 'ticket',
|
||||
scope: {
|
||||
fields: ['id', 'packages', 'warehouseFk', 'nickname', 'clientFk', 'priority', 'addressFk'],
|
||||
order: 'priority',
|
||||
include: [
|
||||
{
|
||||
relation: 'ticketState',
|
||||
scope: {
|
||||
fields: ['id', 'stateFk'],
|
||||
include: [{relation: 'state'}]
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'warehouse',
|
||||
scope: {
|
||||
fields: ['id', 'name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'notes',
|
||||
scope: {
|
||||
where: {observationTypeFk: 3}
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'address',
|
||||
scope: {
|
||||
fields: ['id', 'street', 'postalCode', 'city'],
|
||||
}
|
||||
},
|
||||
Self.getTickets = async(filter, options) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
|
||||
]
|
||||
}
|
||||
}, {
|
||||
relation: 'agencyMode',
|
||||
scope: {
|
||||
fields: ['id', 'name']
|
||||
}
|
||||
}, {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['id', 'userFk'],
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id', 'nickname']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {
|
||||
relation: 'vehicle',
|
||||
scope: {
|
||||
fields: ['id', 'm3', 'numberPlate']
|
||||
}
|
||||
}
|
||||
],
|
||||
};
|
||||
let myOptions = {};
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
route = await Self.app.models.Route.findOne(filter);
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT
|
||||
t.id,
|
||||
t.packages,
|
||||
t.warehouseFk,
|
||||
t.nickname,
|
||||
t.clientFk,
|
||||
t.priority,
|
||||
t.addressFk,
|
||||
st.code AS ticketStateCode,
|
||||
st.name AS ticketStateName,
|
||||
wh.name AS warehouseName,
|
||||
tob.description AS ticketObservation,
|
||||
a.street,
|
||||
a.postalCode,
|
||||
a.city,
|
||||
am.name AS agencyModeName,
|
||||
u.nickname AS userNickname,
|
||||
vn.ticketTotalVolume(t.id) AS volume
|
||||
FROM route r
|
||||
JOIN ticket t ON t.routeFk = r.id
|
||||
LEFT JOIN ticketState ts ON ts.ticketFk = t.id
|
||||
LEFT JOIN state st ON st.id = ts.stateFk
|
||||
LEFT JOIN warehouse wh ON wh.id = t.warehouseFk
|
||||
LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id
|
||||
LEFT JOIN observationType ot ON tob.observationTypeFk = ot.id
|
||||
AND ot.code = 'delivery'
|
||||
LEFT JOIN address a ON a.id = t.addressFk
|
||||
LEFT JOIN agencyMode am ON am.id = t.agencyModeFk
|
||||
LEFT JOIN account.user u ON u.id = r.workerFk
|
||||
LEFT JOIN vehicle v ON v.id = r.vehicleFk`
|
||||
);
|
||||
|
||||
for (let i = 0; i < route.ticket().length; i++) {
|
||||
let ticket = route.ticket()[i];
|
||||
let query = `
|
||||
SELECT vn.ticketTotalVolume(?) AS m3`;
|
||||
if (!filter.where) filter.where = {};
|
||||
|
||||
let options = [ticket.id];
|
||||
let [volume] = await Self.rawSql(query, options);
|
||||
const where = filter.where;
|
||||
where['r.id'] = filter.id;
|
||||
|
||||
ticket.volume = volume.m3;
|
||||
}
|
||||
stmt.merge(conn.makeSuffix(filter));
|
||||
|
||||
return route.ticket();
|
||||
const tickets = await conn.executeStmt(stmt, myOptions);
|
||||
|
||||
return tickets;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2,7 +2,8 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('route getTickets()', () => {
|
||||
it('should return the tickets for a given route', async() => {
|
||||
let result = await app.models.Route.getTickets(2);
|
||||
const filter = {id: 2};
|
||||
let result = await app.models.Route.getTickets(filter);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ module.exports = Self => {
|
|||
};
|
||||
|
||||
summary.route = await Self.app.models.Route.findOne(filter);
|
||||
summary.tickets = await Self.app.models.Route.getTickets(id);
|
||||
summary.tickets = await Self.app.models.Route.getTickets({id: id});
|
||||
|
||||
return summary;
|
||||
};
|
||||
|
|
|
@ -106,9 +106,9 @@
|
|||
</vn-td>
|
||||
<vn-td number shrink>{{ticket.packages}}</vn-td>
|
||||
<vn-td shrink>{{ticket.volume}}</vn-td>
|
||||
<vn-td>{{ticket.warehouse.name}}</vn-td>
|
||||
<vn-td shrink>{{ticket.address.postalCode}}</vn-td>
|
||||
<vn-td expand title="{{ticket.address.street}}">{{ticket.address.street}}</vn-td>
|
||||
<vn-td>{{ticket.warehouseName}}</vn-td>
|
||||
<vn-td shrink>{{ticket.postalCode}}</vn-td>
|
||||
<vn-td expand title="{{ticket.address.street}}">{{ticket.street}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon
|
||||
ng-if="ticket.notes.length"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Routes/{{$ctrl.$params.id}}/getTickets"
|
||||
url="Routes/getTickets"
|
||||
filter="{id: $ctrl.$params.id}"
|
||||
order="priority ASC"
|
||||
data="$ctrl.tickets"
|
||||
auto-load="true">
|
||||
|
@ -34,14 +35,14 @@
|
|||
model="model">
|
||||
</vn-multi-check>
|
||||
</vn-th>
|
||||
<vn-th>Order</vn-th>
|
||||
<vn-th expand>Street</vn-th>
|
||||
<vn-th>City</vn-th>
|
||||
<vn-th translate-attr="{title: 'Postcode'}" shrink>PC</vn-th>
|
||||
<vn-th expand>Client</vn-th>
|
||||
<vn-th shrink>Packages</vn-th>
|
||||
<vn-th shrink>m³</vn-th>
|
||||
<vn-th number>Ticket</vn-th>
|
||||
<vn-th field="priority">Order</vn-th>
|
||||
<vn-th field="street" expand>Street</vn-th>
|
||||
<vn-th field="city">City</vn-th>
|
||||
<vn-th field="postalCode" translate-attr="{title: 'Postcode'}" shrink>PC</vn-th>
|
||||
<vn-th field="clientFk" expand>Client</vn-th>
|
||||
<vn-th field="packages" shrink>Packages</vn-th>
|
||||
<vn-th field="volume" shrink>m³</vn-th>
|
||||
<vn-th field="id" number>Ticket</vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
</vn-tr>
|
||||
|
@ -62,9 +63,9 @@
|
|||
display-controls=true>
|
||||
</vn-input-number>
|
||||
</vn-td>
|
||||
<vn-td expand title="{{ticket.address.street}}">{{ticket.address.street}}</vn-td>
|
||||
<vn-td expand>{{ticket.address.city}}</vn-td>
|
||||
<vn-td shrink>{{ticket.address.postalCode}}</vn-td>
|
||||
<vn-td expand title="{{ticket.street}}">{{::ticket.street}}</vn-td>
|
||||
<vn-td expand>{{::ticket.city}}</vn-td>
|
||||
<vn-td shrink>{{::ticket.postalCode}}</vn-td>
|
||||
<vn-td expand>
|
||||
<span
|
||||
ng-click="clientDescriptor.show($event, ticket.clientFk)"
|
||||
|
@ -72,13 +73,13 @@
|
|||
{{ticket.nickname}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>{{ticket.packages}}</vn-td>
|
||||
<vn-td shrink>{{::ticket.packages}}</vn-td>
|
||||
<vn-td shrink>{{::ticket.volume | number:1}}</vn-td>
|
||||
<vn-td number>
|
||||
<span
|
||||
ng-click="ticketDescriptor.show($event, ticket.id)"
|
||||
class="link">
|
||||
{{ticket.id}}
|
||||
{{::ticket.id}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td shrink>
|
||||
|
@ -112,7 +113,7 @@
|
|||
<vn-confirm
|
||||
vn-id="confirm"
|
||||
question="Delete ticket from route?"
|
||||
on-accept="$ctrl.removeTicketFromRoute()">
|
||||
on-accept="$ctrl.removeTicketFromRoute($index)">
|
||||
</vn-confirm>
|
||||
<vn-crud-model
|
||||
vn-id="possibleTicketsModel"
|
||||
|
|
|
@ -66,7 +66,7 @@ class Controller extends Section {
|
|||
|
||||
let url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
|
||||
lines.forEach(line => {
|
||||
addresses = addresses + '+to:' + line.address.postalCode + ' ' + line.address.city + ' ' + line.address.street;
|
||||
addresses = addresses + '+to:' + line.postalCode + ' ' + line.city + ' ' + line.street;
|
||||
});
|
||||
|
||||
window.open(url + addresses, '_blank');
|
||||
|
@ -78,10 +78,11 @@ class Controller extends Section {
|
|||
this.$.confirm.show();
|
||||
}
|
||||
|
||||
removeTicketFromRoute() {
|
||||
removeTicketFromRoute($index) {
|
||||
let params = {routeFk: null};
|
||||
let query = `Tickets/${this.selectedTicket}/`;
|
||||
this.$http.patch(query, params).then(() => {
|
||||
this.$.model.remove($index);
|
||||
this.vnApp.showSuccess(this.$t('Ticket removed from route'));
|
||||
this.updateVolume();
|
||||
});
|
||||
|
|
|
@ -13,7 +13,10 @@ describe('Route', () => {
|
|||
const $element = angular.element('<vn-route-tickets></vn-route-tickets>');
|
||||
controller = $componentController('vnRouteTickets', {$element, $scope});
|
||||
controller.route = {id: 1};
|
||||
controller.$.model = {refresh: () => {}};
|
||||
controller.$.model = {
|
||||
refresh: () => {},
|
||||
remove: () => {}
|
||||
};
|
||||
controller.card = {reload: () => {}};
|
||||
}));
|
||||
|
||||
|
@ -98,11 +101,9 @@ describe('Route', () => {
|
|||
{
|
||||
id: 1,
|
||||
checked: true,
|
||||
address: {
|
||||
street: 'my street',
|
||||
postalCode: 'n19',
|
||||
city: 'London'
|
||||
}
|
||||
street: 'my street',
|
||||
postalCode: 'n19',
|
||||
city: 'London'
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -130,6 +131,8 @@ describe('Route', () => {
|
|||
it('should perform a patch query then call showSuccess and updateVolume methods', () => {
|
||||
jest.spyOn(controller, 'updateVolume').mockReturnThis();
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
jest.spyOn(controller.$.model, 'remove');
|
||||
|
||||
let ticketId = 1;
|
||||
controller.selectedTicket = ticketId;
|
||||
|
||||
|
|
|
@ -9,48 +9,57 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
type: 'object',
|
||||
http: {source: 'context'}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
type: 'object',
|
||||
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'search',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: `If it's and integer searchs by id, otherwise it searchs by nickname`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'ticketFk',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: `Searchs by ticketFk`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'warehouseFk',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: `Search by warehouse`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'attenderFk',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: `Search requests attended by a given worker id`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'mine',
|
||||
type: 'Boolean',
|
||||
type: 'boolean',
|
||||
description: `Search requests attended by the current user`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'from',
|
||||
type: 'Date',
|
||||
type: 'date',
|
||||
description: `Date from`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'to',
|
||||
type: 'Date',
|
||||
type: 'date',
|
||||
description: `Date to`
|
||||
}, {
|
||||
},
|
||||
{
|
||||
arg: 'state',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: `Search request by request state`
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
|
|
@ -5,65 +5,76 @@ module.exports = Self => {
|
|||
Self.remoteMethodCtx('componentUpdate', {
|
||||
description: 'Save ticket sale components',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The ticket id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'clientFk',
|
||||
type: 'Number',
|
||||
description: 'The client id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'agencyModeFk',
|
||||
type: 'Number',
|
||||
description: 'The agencyMode id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'addressFk',
|
||||
type: 'Number',
|
||||
description: 'The address id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'zoneFk',
|
||||
type: 'Number',
|
||||
description: 'The zone id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'warehouseFk',
|
||||
type: 'Number',
|
||||
description: 'The warehouse id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'companyFk',
|
||||
type: 'Number',
|
||||
description: 'The company id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'shipped',
|
||||
type: 'Date',
|
||||
description: 'The shipped date',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'landed',
|
||||
type: 'Date',
|
||||
description: 'The landing date',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'isDeleted',
|
||||
type: 'Boolean',
|
||||
description: 'Ticket is deleted',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'option',
|
||||
type: 'Number',
|
||||
description: 'Action id',
|
||||
required: true
|
||||
}],
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The ticket id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'clientFk',
|
||||
type: 'number',
|
||||
description: 'The client id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'agencyModeFk',
|
||||
type: 'number',
|
||||
description: 'The agencyMode id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'addressFk',
|
||||
type: 'number',
|
||||
description: 'The address id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'zoneFk',
|
||||
type: 'number',
|
||||
description: 'The zone id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'warehouseFk',
|
||||
type: 'number',
|
||||
description: 'The warehouse id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'companyFk',
|
||||
type: 'number',
|
||||
description: 'The company id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'shipped',
|
||||
type: 'date',
|
||||
description: 'The shipped date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'landed',
|
||||
type: 'date',
|
||||
description: 'The landing date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'isDeleted',
|
||||
type: 'boolean',
|
||||
description: 'Ticket is deleted',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'option',
|
||||
type: 'number',
|
||||
description: 'Action id',
|
||||
required: true
|
||||
}],
|
||||
returns: {
|
||||
type: ['Object'],
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
@ -72,128 +83,156 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.componentUpdate = async(ctx, id, clientFk, agencyModeFk, addressFk, zoneFk, warehouseFk,
|
||||
companyFk, shipped, landed, isDeleted, option) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const models = Self.app.models;
|
||||
const $t = ctx.req.__; // $translate
|
||||
const isEditable = await models.Ticket.isEditable(ctx, id);
|
||||
Self.componentUpdate = async(ctx, options) => {
|
||||
const args = ctx.args;
|
||||
let tx;
|
||||
let myOptions = {};
|
||||
|
||||
if (!isEditable)
|
||||
throw new UserError(`The sales of this ticket can't be modified`);
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss');
|
||||
if (!isProductionBoss) {
|
||||
const zoneShipped = await models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk);
|
||||
|
||||
if (!zoneShipped || zoneShipped.zoneFk != zoneFk)
|
||||
throw new UserError(`You don't have privileges to change the zone`);
|
||||
if (!myOptions.transaction) {
|
||||
tx = await Self.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
const observationTypeDelivery = await models.ObservationType.findOne({
|
||||
where: {code: 'delivery'}
|
||||
});
|
||||
|
||||
const originalTicket = await models.Ticket.findOne({
|
||||
where: {id: id},
|
||||
fields: ['id', 'clientFk', 'agencyModeFk', 'addressFk', 'zoneFk',
|
||||
'warehouseFk', 'companyFk', 'shipped', 'landed', 'isDeleted'],
|
||||
include: [
|
||||
{
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: 'salesPersonFk'
|
||||
}
|
||||
}]
|
||||
});
|
||||
const updatedTicket = Object.assign({}, ctx.args);
|
||||
delete updatedTicket.ctx;
|
||||
delete updatedTicket.option;
|
||||
try {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const models = Self.app.models;
|
||||
const $t = ctx.req.__; // $translate
|
||||
const isEditable = await models.Ticket.isEditable(ctx, args.id, myOptions);
|
||||
|
||||
// Force to unroute ticket
|
||||
const hasToBeUnrouted = true;
|
||||
const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
const res = await Self.rawSql(query, [
|
||||
id,
|
||||
clientFk,
|
||||
agencyModeFk,
|
||||
addressFk,
|
||||
zoneFk,
|
||||
warehouseFk,
|
||||
companyFk,
|
||||
shipped,
|
||||
landed,
|
||||
isDeleted,
|
||||
hasToBeUnrouted,
|
||||
option
|
||||
]);
|
||||
if (!isEditable)
|
||||
throw new UserError(`The sales of this ticket can't be modified`);
|
||||
|
||||
if (originalTicket.addressFk != updatedTicket.addressFk) {
|
||||
const ticketObservation = await models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: id,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
});
|
||||
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss', myOptions);
|
||||
if (!isProductionBoss) {
|
||||
const zoneShipped = await models.Agency.getShipped(args.landed, args.addressFk, args.agencyModeFk, args.warehouseFk, myOptions);
|
||||
|
||||
if (ticketObservation)
|
||||
await ticketObservation.destroy();
|
||||
if (!zoneShipped || zoneShipped.zoneFk != args.zoneFk)
|
||||
throw new UserError(`You don't have privileges to change the zone`);
|
||||
}
|
||||
const observationTypeDelivery = await models.ObservationType.findOne({
|
||||
where: {code: 'delivery'}
|
||||
}, myOptions);
|
||||
|
||||
const address = await models.Address.findOne({
|
||||
where: {id: addressFk},
|
||||
include: {
|
||||
relation: 'observations',
|
||||
scope: {
|
||||
where: {observationTypeFk: observationTypeDelivery.id},
|
||||
include: {
|
||||
relation: 'observationType'
|
||||
const originalTicket = await models.Ticket.findOne({
|
||||
where: {id: args.id},
|
||||
fields: [
|
||||
'id',
|
||||
'clientFk',
|
||||
'agencyModeFk',
|
||||
'addressFk',
|
||||
'zoneFk',
|
||||
'warehouseFk',
|
||||
'companyFk',
|
||||
'shipped',
|
||||
'landed',
|
||||
'isDeleted'
|
||||
],
|
||||
include: [
|
||||
{
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: 'salesPersonFk'
|
||||
}
|
||||
}]
|
||||
}, myOptions);
|
||||
|
||||
const updatedTicket = Object.assign({}, args);
|
||||
delete updatedTicket.ctx;
|
||||
delete updatedTicket.option;
|
||||
|
||||
// Force to unroute ticket
|
||||
const hasToBeUnrouted = true;
|
||||
const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
const res = await Self.rawSql(query, [
|
||||
args.id,
|
||||
args.clientFk,
|
||||
args.agencyModeFk,
|
||||
args.addressFk,
|
||||
args.zoneFk,
|
||||
args.warehouseFk,
|
||||
args.companyFk,
|
||||
args.shipped,
|
||||
args.landed,
|
||||
args.isDeleted,
|
||||
hasToBeUnrouted,
|
||||
args.option
|
||||
], myOptions);
|
||||
|
||||
if (originalTicket.addressFk != updatedTicket.addressFk) {
|
||||
const ticketObservation = await models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: args.id,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
}, myOptions);
|
||||
|
||||
if (ticketObservation)
|
||||
await ticketObservation.destroy(myOptions);
|
||||
|
||||
const address = await models.Address.findOne({
|
||||
where: {id: args.addressFk},
|
||||
include: {
|
||||
relation: 'observations',
|
||||
scope: {
|
||||
where: {observationTypeFk: observationTypeDelivery.id},
|
||||
include: {
|
||||
relation: 'observationType'
|
||||
}
|
||||
}
|
||||
}
|
||||
}, myOptions);
|
||||
const [observation] = address.observations();
|
||||
if (observation) {
|
||||
await models.TicketObservation.create({
|
||||
ticketFk: args.id,
|
||||
observationTypeFk: observation.observationTypeFk,
|
||||
description: observation.description
|
||||
}, myOptions);
|
||||
}
|
||||
});
|
||||
const [observation] = address.observations();
|
||||
if (observation) {
|
||||
await models.TicketObservation.create({
|
||||
ticketFk: id,
|
||||
observationTypeFk: observation.observationTypeFk,
|
||||
description: observation.description
|
||||
}
|
||||
|
||||
const changes = loggable.getChanges(originalTicket, updatedTicket);
|
||||
const oldProperties = await loggable.translateValues(Self, changes.old);
|
||||
const newProperties = await loggable.translateValues(Self, changes.new);
|
||||
|
||||
await models.TicketLog.create({
|
||||
originFk: args.id,
|
||||
userFk: userId,
|
||||
action: 'update',
|
||||
changedModel: 'Ticket',
|
||||
changedModelId: args.id,
|
||||
oldInstance: oldProperties,
|
||||
newInstance: newProperties
|
||||
}, myOptions);
|
||||
|
||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||
if (salesPersonId) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
|
||||
let changesMade = '';
|
||||
for (let change in newProperties) {
|
||||
let value = newProperties[change];
|
||||
let oldValue = oldProperties[change];
|
||||
|
||||
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`;
|
||||
}
|
||||
|
||||
const message = $t('Changed this data from the ticket', {
|
||||
ticketId: args.id,
|
||||
ticketUrl: `${origin}/#!/ticket/${args.id}/summary`,
|
||||
changes: changesMade
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const changes = loggable.getChanges(originalTicket, updatedTicket);
|
||||
const oldProperties = await loggable.translateValues(Self, changes.old);
|
||||
const newProperties = await loggable.translateValues(Self, changes.new);
|
||||
|
||||
await models.TicketLog.create({
|
||||
originFk: id,
|
||||
userFk: userId,
|
||||
action: 'update',
|
||||
changedModel: 'Ticket',
|
||||
changedModelId: id,
|
||||
oldInstance: oldProperties,
|
||||
newInstance: newProperties
|
||||
});
|
||||
|
||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||
if (salesPersonId) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
|
||||
let changesMade = '';
|
||||
for (let change in newProperties) {
|
||||
let value = newProperties[change];
|
||||
let oldValue = oldProperties[change];
|
||||
|
||||
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`;
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message, myOptions);
|
||||
}
|
||||
|
||||
const message = $t('Changed this data from the ticket', {
|
||||
ticketId: id,
|
||||
ticketUrl: `${origin}/#!/ticket/${id}/summary`,
|
||||
changes: changesMade
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||
}
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return res;
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -19,15 +19,19 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.isEditable = async(ctx, id) => {
|
||||
Self.isEditable = async(ctx, id, options) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
let state = await Self.app.models.TicketState.findOne({
|
||||
where: {ticketFk: id}
|
||||
});
|
||||
}, myOptions);
|
||||
|
||||
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant');
|
||||
const isProductionBoss = await Self.app.models.Account.hasRole(userId, 'productionBoss');
|
||||
const isSalesAssistant = await Self.app.models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
||||
const isProductionBoss = await Self.app.models.Account.hasRole(userId, 'productionBoss', myOptions);
|
||||
const isValidRole = isSalesAssistant || isProductionBoss;
|
||||
|
||||
let alertLevel = state ? state.alertLevel : null;
|
||||
|
@ -41,8 +45,8 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
const isLocked = await Self.app.models.Ticket.isLocked(id);
|
||||
}, myOptions);
|
||||
const isLocked = await Self.app.models.Ticket.isLocked(id, myOptions);
|
||||
|
||||
const alertLevelGreaterThanZero = (alertLevel && alertLevel > 0);
|
||||
const isNormalClient = ticket && ticket.client().type().code == 'normal';
|
||||
|
|
|
@ -19,10 +19,15 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.isLocked = async id => {
|
||||
Self.isLocked = async(id, options) => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const ticket = await Self.app.models.Ticket.findById(id, {
|
||||
fields: ['isDeleted', 'refFk']
|
||||
});
|
||||
}, myOptions);
|
||||
|
||||
const isDeleted = ticket && ticket.isDeleted;
|
||||
const isInvoiced = ticket && ticket.refFk;
|
||||
|
|
|
@ -13,147 +13,125 @@ describe('ticket componentUpdate()', () => {
|
|||
let secondvalueBeforeChange;
|
||||
let componentOfSaleSeven;
|
||||
let componentOfSaleEight;
|
||||
let componentValue;
|
||||
|
||||
beforeAll(async() => {
|
||||
const deliveryComponenet = await app.models.Component.findOne({where: {code: 'delivery'}});
|
||||
deliveryComponentId = deliveryComponenet.id;
|
||||
componentOfSaleSeven = `SELECT value FROM vn.saleComponent WHERE saleFk = 7 AND componentFk = ${deliveryComponentId}`;
|
||||
componentOfSaleEight = `SELECT value FROM vn.saleComponent WHERE saleFk = 8 AND componentFk = ${deliveryComponentId}`;
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
|
||||
firstvalueBeforeChange = componentValue.value;
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight);
|
||||
secondvalueBeforeChange = componentValue.value;
|
||||
});
|
||||
|
||||
it('should change the agencyMode to modify the sale components value', async() => {
|
||||
const tx = await app.models.SaleComponent.beginTransaction({});
|
||||
|
||||
beforeAll(async done => {
|
||||
try {
|
||||
let deliveryComponenet = await app.models.Component.findOne({where: {code: 'delivery'}});
|
||||
deliveryComponentId = deliveryComponenet.id;
|
||||
componentOfSaleSeven = `SELECT value FROM vn.saleComponent WHERE saleFk = 7 AND componentFk = ${deliveryComponentId}`;
|
||||
componentOfSaleEight = `SELECT value FROM vn.saleComponent WHERE saleFk = 8 AND componentFk = ${deliveryComponentId}`;
|
||||
const options = {transaction: tx};
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
|
||||
firstvalueBeforeChange = componentValue.value;
|
||||
const args = {
|
||||
id: ticketID,
|
||||
clientFk: 102,
|
||||
agencyModeFk: 8,
|
||||
addressFk: 122,
|
||||
zoneFk: 5,
|
||||
warehouseFk: 1,
|
||||
companyFk: 442,
|
||||
shipped: today,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1
|
||||
};
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight);
|
||||
secondvalueBeforeChange = componentValue.value;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
let ctx = {
|
||||
args: args,
|
||||
req: {
|
||||
accessToken: {userId: userID},
|
||||
headers: {origin: 'http://localhost'},
|
||||
__: value => {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await app.models.Ticket.componentUpdate(ctx, options);
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven, null, options);
|
||||
let firstvalueAfterChange = componentValue.value;
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight, null, options);
|
||||
let secondvalueAfterChange = componentValue.value;
|
||||
|
||||
expect(firstvalueBeforeChange).not.toEqual(firstvalueAfterChange);
|
||||
expect(secondvalueBeforeChange).not.toEqual(secondvalueAfterChange);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should change the agencyMode to modify the sale components value and then undo the changes', async() => {
|
||||
const clientID = 102;
|
||||
const addressID = 122;
|
||||
const agencyModeID = 8;
|
||||
const warehouseID = 1;
|
||||
const zoneID = 5;
|
||||
const shipped = today;
|
||||
const companyID = 442;
|
||||
const isDeleted = false;
|
||||
const landed = tomorrow;
|
||||
const option = 1;
|
||||
it('should change the addressFk and check that delivery observations have been changed', async() => {
|
||||
const tx = await app.models.SaleComponent.beginTransaction({});
|
||||
|
||||
let ctx = {
|
||||
args: {clientFk: clientID,
|
||||
agencyModeFk: agencyModeID},
|
||||
req: {
|
||||
accessToken: {userId: userID},
|
||||
headers: {origin: 'http://localhost'},
|
||||
__: value => {
|
||||
return value;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const args = {
|
||||
id: ticketID,
|
||||
clientFk: 102,
|
||||
agencyModeFk: 8,
|
||||
addressFk: 2,
|
||||
zoneFk: 5,
|
||||
warehouseFk: 1,
|
||||
companyFk: 442,
|
||||
shipped: today,
|
||||
landed: tomorrow,
|
||||
isDeleted: false,
|
||||
option: 1
|
||||
};
|
||||
|
||||
const ctx = {
|
||||
args: args,
|
||||
req: {
|
||||
accessToken: {userId: userID},
|
||||
headers: {origin: 'http://localhost'},
|
||||
__: value => {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
const observationTypeDelivery = await app.models.ObservationType.findOne({
|
||||
where: {code: 'delivery'}
|
||||
}, options);
|
||||
const originalTicketObservation = await app.models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: args.id,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
}, options);
|
||||
|
||||
await app.models.Ticket.componentUpdate(ctx, ticketID, clientID, agencyModeID, addressID,
|
||||
zoneID, warehouseID, companyID, shipped, landed, isDeleted, option);
|
||||
expect(originalTicketObservation).toBeDefined();
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
|
||||
let firstvalueAfterChange = componentValue.value;
|
||||
await app.models.Ticket.componentUpdate(ctx, options);
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight);
|
||||
let secondvalueAfterChange = componentValue.value;
|
||||
const removedTicketObservation = await app.models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: ticketID,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
}, options);
|
||||
|
||||
expect(firstvalueBeforeChange).not.toEqual(firstvalueAfterChange);
|
||||
expect(secondvalueBeforeChange).not.toEqual(secondvalueAfterChange);
|
||||
expect(removedTicketObservation).toBeNull();
|
||||
|
||||
// restores
|
||||
const restores = {
|
||||
clientID: 102,
|
||||
addressID: 122,
|
||||
agencyModeID: 7,
|
||||
warehouseID: 1,
|
||||
zoneID: 3,
|
||||
shipped: today,
|
||||
companyID: 442,
|
||||
isDeleted: false,
|
||||
landed: tomorrow,
|
||||
option: 1,
|
||||
};
|
||||
|
||||
ctx.clientFk = restores.clientID;
|
||||
ctx.agencyModeFk = restores.agencyModeID;
|
||||
|
||||
await app.models.Ticket.componentUpdate(ctx, ticketID, restores.clientID, restores.agencyModeID, restores.addressID,
|
||||
restores.zoneID, restores.warehouseID, restores.companyID, restores.shipped, restores.landed, restores.isDeleted, restores.option);
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleSeven);
|
||||
firstvalueAfterChange = componentValue.value;
|
||||
|
||||
[componentValue] = await app.models.SaleComponent.rawSql(componentOfSaleEight);
|
||||
secondvalueAfterChange = componentValue.value;
|
||||
|
||||
expect(firstvalueBeforeChange).toEqual(firstvalueAfterChange);
|
||||
expect(secondvalueBeforeChange).toEqual(secondvalueAfterChange);
|
||||
});
|
||||
|
||||
it('should change the addressFk and check that delivery observations have been changed and then undo the changes', async() => {
|
||||
const clientID = 102;
|
||||
const addressID = 122;
|
||||
const newAddressID = 2;
|
||||
const agencyModeID = 8;
|
||||
const warehouseID = 1;
|
||||
const zoneID = 5;
|
||||
const shipped = today;
|
||||
const companyID = 442;
|
||||
const isDeleted = false;
|
||||
const landed = tomorrow;
|
||||
const option = 1;
|
||||
const ctx = {
|
||||
args: {clientFk: clientID,
|
||||
agencyModeFk: agencyModeID},
|
||||
req: {
|
||||
accessToken: {userId: userID},
|
||||
headers: {origin: 'http://localhost'},
|
||||
__: value => {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
const observationTypeDelivery = await app.models.ObservationType.findOne({
|
||||
where: {code: 'delivery'}
|
||||
});
|
||||
const originalTicketObservation = await app.models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: ticketID,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
});
|
||||
|
||||
expect(originalTicketObservation).toBeDefined();
|
||||
|
||||
await app.models.Ticket.componentUpdate(ctx, ticketID, clientID, agencyModeID, newAddressID,
|
||||
zoneID, warehouseID, companyID, shipped, landed, isDeleted, option);
|
||||
|
||||
const removedTicketObservation = await app.models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: ticketID,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
});
|
||||
|
||||
expect(removedTicketObservation).toBeNull();
|
||||
|
||||
// restores
|
||||
await app.models.Ticket.componentUpdate(ctx, ticketID, clientID, agencyModeID, addressID,
|
||||
zoneID, warehouseID, companyID, shipped, landed, isDeleted, option);
|
||||
|
||||
const restoredTicketObservation = await app.models.TicketObservation.findOne({
|
||||
where: {
|
||||
ticketFk: ticketID,
|
||||
observationTypeFk: observationTypeDelivery.id}
|
||||
});
|
||||
|
||||
expect(restoredTicketObservation.description).toEqual(originalTicketObservation.description);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,52 +2,135 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('ticket isEditable()', () => {
|
||||
it('should return false if the given ticket does not exist', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 9}}};
|
||||
let result = await app.models.Ticket.isEditable(ctx, 99999);
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
let result;
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 9}}
|
||||
};
|
||||
|
||||
result = await app.models.Ticket.isEditable(ctx, 9999, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it(`should return false if the given ticket isn't invoiced but isDeleted`, async() => {
|
||||
let ctx = {req: {accessToken: {userId: 9}}};
|
||||
let deletedTicket = await app.models.Ticket.findOne({
|
||||
where: {
|
||||
invoiceOut: null,
|
||||
isDeleted: true
|
||||
},
|
||||
fields: ['id']
|
||||
});
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
let result;
|
||||
|
||||
let result = await app.models.Ticket.isEditable(ctx, deletedTicket.id);
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const deletedTicket = await app.models.Ticket.findOne({
|
||||
where: {
|
||||
invoiceOut: null,
|
||||
isDeleted: true
|
||||
},
|
||||
fields: ['id']
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 9}}
|
||||
};
|
||||
|
||||
result = await app.models.Ticket.isEditable(ctx, deletedTicket.id, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return true if the given ticket is editable', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 9}}};
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
let result;
|
||||
|
||||
let result = await app.models.Ticket.isEditable(ctx, 16);
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 9}}
|
||||
};
|
||||
|
||||
result = await app.models.Ticket.isEditable(ctx, 16, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not be able to edit a deleted or invoiced ticket even for salesAssistant', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 21}}};
|
||||
let result = await app.models.Ticket.isEditable(ctx, 19);
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
let result;
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 21}}
|
||||
};
|
||||
|
||||
result = await app.models.Ticket.isEditable(ctx, 19, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not be able to edit a deleted or invoiced ticket even for productionBoss', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 50}}};
|
||||
let result = await app.models.Ticket.isEditable(ctx, 19);
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
let result;
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 50}}
|
||||
};
|
||||
|
||||
result = await app.models.Ticket.isEditable(ctx, 19, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not be able to edit a deleted or invoiced ticket even for salesPerson', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 18}}};
|
||||
let result = await app.models.Ticket.isEditable(ctx, 19);
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
let result;
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: 18}}
|
||||
};
|
||||
|
||||
result = await app.models.Ticket.isEditable(ctx, 19, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
|
|
@ -34,7 +34,12 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.getShipped = async(landed, addressFk, agencyModeFk, warehouseFk)=> {
|
||||
Self.getShipped = async(landed, addressFk, agencyModeFk, warehouseFk, options) => {
|
||||
let myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
let stmts = [];
|
||||
stmts.push(new ParameterizedSQL(
|
||||
`CALL vn.zone_getShippedWarehouse(?, ?, ?)`, [
|
||||
|
@ -44,14 +49,14 @@ module.exports = Self => {
|
|||
]
|
||||
));
|
||||
|
||||
let rsIndex = stmts.push(new ParameterizedSQL(
|
||||
const rsIndex = stmts.push(new ParameterizedSQL(
|
||||
`SELECT * FROM tmp.zoneGetShipped WHERE warehouseFk = ?`, [
|
||||
warehouseFk
|
||||
]
|
||||
)) - 1;
|
||||
|
||||
let sql = ParameterizedSQL.join(stmts, ';');
|
||||
let shipped = await Self.rawStmt(sql);
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const shipped = await Self.rawStmt(sql, myOptions);
|
||||
|
||||
return shipped[rsIndex][0];
|
||||
};
|
||||
|
|
|
@ -2,27 +2,49 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('agency getShipped()', () => {
|
||||
it('should return a shipment date', async() => {
|
||||
const landed = new Date();
|
||||
landed.setDate(landed.getDate() + 1);
|
||||
const addressFk = 121;
|
||||
const agencyModeFk = 7;
|
||||
const warehouseFk = 1;
|
||||
const tx = await app.models.Agency.beginTransaction({});
|
||||
let result;
|
||||
|
||||
let result = await app.models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk);
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const landed = new Date();
|
||||
landed.setDate(landed.getDate() + 1);
|
||||
const addressFk = 121;
|
||||
const agencyModeFk = 7;
|
||||
const warehouseFk = 1;
|
||||
|
||||
result = await app.models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not return a shipment date', async() => {
|
||||
let newDate = new Date();
|
||||
newDate.setMonth(newDate.getMonth() - 1);
|
||||
const tx = await app.models.Agency.beginTransaction({});
|
||||
let result;
|
||||
|
||||
const landed = newDate;
|
||||
const addressFk = 121;
|
||||
const agencyModeFk = 7;
|
||||
const warehouseFk = 1;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
let newDate = new Date();
|
||||
newDate.setMonth(newDate.getMonth() - 1);
|
||||
const landed = newDate;
|
||||
const addressFk = 121;
|
||||
const agencyModeFk = 7;
|
||||
const warehouseFk = 1;
|
||||
|
||||
let result = await app.models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk);
|
||||
result = await app.models.Agency.getShipped(landed, addressFk, agencyModeFk, warehouseFk, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue