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

Reviewed-on: #623
Reviewed-by: Joan Sanchez <joan@verdnatura.es>
This commit is contained in:
Joan Sanchez 2021-05-17 10:32:42 +00:00
commit ea1ff3e5fa
27 changed files with 746 additions and 514 deletions

View File

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

View File

@ -1 +0,0 @@
Delete me

View File

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

View File

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

View File

@ -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();

View File

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

View File

@ -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();

View File

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

View File

@ -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() => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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