Merge branch 'dev' into 6830-item_getLackOrigin
gitea/salix/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Ivan Mas 2024-02-13 13:12:02 +00:00
commit add5fedc4e
60 changed files with 975 additions and 432 deletions

View File

@ -49,7 +49,6 @@ module.exports = Self => {
Self.uploadFile = async(ctx, options) => { Self.uploadFile = async(ctx, options) => {
const models = Self.app.models; const models = Self.app.models;
const TempContainer = models.TempContainer; const TempContainer = models.TempContainer;
const DmsContainer = models.DmsContainer;
const fileOptions = {}; const fileOptions = {};
const args = ctx.args; const args = ctx.args;
@ -79,19 +78,21 @@ module.exports = Self => {
const addedDms = []; const addedDms = [];
for (const uploadedFile of files) { for (const uploadedFile of files) {
const newDms = await createDms(ctx, uploadedFile, myOptions);
const pathHash = DmsContainer.getHash(newDms.id);
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name); const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
srcFile = path.join(file.client.root, file.container, file.name); srcFile = path.join(file.client.root, file.container, file.name);
const dmsContainer = await DmsContainer.container(pathHash); const data = {
const dstFile = path.join(dmsContainer.client.root, pathHash, newDms.file); workerFk: ctx.req.accessToken.userId,
dmsTypeFk: args.dmsTypeId,
await fs.move(srcFile, dstFile, { companyFk: args.companyId,
overwrite: true warehouseFk: args.warehouseId,
}); reference: args.reference,
description: args.description,
contentType: args.contentType,
hasFile: args.hasFile
};
const extension = await models.DmsContainer.getFileExtension(uploadedFile.name);
const newDms = await Self.createFromFile(data, extension, srcFile, myOptions);
addedDms.push(newDms); addedDms.push(newDms);
} }
@ -107,27 +108,4 @@ module.exports = Self => {
throw e; throw e;
} }
}; };
async function createDms(ctx, file, myOptions) {
const models = Self.app.models;
const myUserId = ctx.req.accessToken.userId;
const args = ctx.args;
const newDms = await Self.create({
workerFk: myUserId,
dmsTypeFk: args.dmsTypeId,
companyFk: args.companyId,
warehouseFk: args.warehouseId,
reference: args.reference,
description: args.description,
contentType: file.type,
hasFile: args.hasFile
}, myOptions);
let fileName = file.name;
const extension = models.DmsContainer.getFileExtension(fileName);
fileName = `${newDms.id}.${extension}`;
return newDms.updateAttribute('file', fileName, myOptions);
}
}; };

View File

@ -1,4 +1,6 @@
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
const fs = require('fs-extra');
const path = require('path');
module.exports = Self => { module.exports = Self => {
require('../methods/dms/downloadFile')(Self); require('../methods/dms/downloadFile')(Self);
@ -35,4 +37,32 @@ module.exports = Self => {
return [stream, dms.contentType, `filename="${dms.file}"`]; return [stream, dms.contentType, `filename="${dms.file}"`];
}; };
Self.getPath = async function(dms) {
const models = Self.app.models;
const pathHash = await models.DmsContainer.getHash(dms.id);
const dmsContainer = await models.DmsContainer.container(pathHash);
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
return dstFile;
};
Self.createWithExtension = async function(data, extension, options) {
const newDms = await Self.create(data, options);
return newDms.updateAttribute('file', `${newDms.id}.${extension}`, options);
};
Self.createFromFile = async function(data, extension, srcFile, options) {
const dms = await Self.createWithExtension(data, extension, options);
const dstFile = await Self.getPath(dms);
await fs.move(srcFile, dstFile, {overwrite: true});
return dms;
};
Self.createFromStream = async function(data, extension, stream, options) {
const dms = await Self.createWithExtension(data, extension, options);
const dstFile = await Self.getPath(dms);
const writeStream = await fs.createWriteStream(dstFile);
await stream.pipe(writeStream);
return dms;
};
}; };

View File

@ -17,7 +17,8 @@ const opts = getopts(process.argv.slice(2), {
let server; let server;
const PARALLEL = false; const PARALLEL = false;
const TIMEOUT = 900000; const SETUP_TIMEOUT = 15 * 60 * 1000;
const SPEC_TIMEOUT = 30 * 1000;
process.on('exit', teardown); process.on('exit', teardown);
process.on('uncaughtException', onError); process.on('uncaughtException', onError);
@ -74,9 +75,9 @@ async function test() {
let runner; let runner;
const config = { const config = {
globalSetup: setup, globalSetup: setup,
globalSetupTimeout: TIMEOUT, globalSetupTimeout: SETUP_TIMEOUT,
globalTeardown: teardown, globalTeardown: teardown,
globalTeardownTimeout: TIMEOUT, globalTeardownTimeout: SETUP_TIMEOUT,
spec_dir: '.', spec_dir: '.',
spec_files: [ spec_files: [
'back/**/*[sS]pec.js', 'back/**/*[sS]pec.js',
@ -111,10 +112,11 @@ async function test() {
runner.addReporter(new JunitReporter.JUnitXmlReporter()); runner.addReporter(new JunitReporter.JUnitXmlReporter());
} }
if (opts.ci) if (opts.ci)
runner.jasmine.DEFAULT_TIMEOUT_INTERVAL = TIMEOUT; runner.jasmine.DEFAULT_TIMEOUT_INTERVAL = SPEC_TIMEOUT;
// runner.loadConfigFile('back/jasmine.json'); // runner.loadConfigFile('back/jasmine.json');
runner.loadConfig(config); runner.loadConfig(config);
process.env.SPEC_IS_RUNNING = true;
await runner.execute(); await runner.execute();
} }

View File

@ -66,9 +66,6 @@ UPDATE vn.supplier
SET isTrucker = 1 SET isTrucker = 1
WHERE id = 2; WHERE id = 2;
INSERT INTO vn.cmr (id, truckPlate, observations, senderInstruccions, paymentInstruccions, specialAgreements, created, companyFk, addressToFk, addressFromFk, supplierFk, packagesList, merchandiseDetail, state, landed, ead)
VALUES (2, NULL, NULL, NULL, 'Carriage paid', NULL, '2022-06-27 13:31:11.000', 442, 3, 2, 2, NULL, NULL, NULL, NULL, NULL);
-- XXX: tpv -- XXX: tpv
UPDATE `vn`.`claimRatio` SET `claimAmount` = '10' WHERE (`clientFk` = '1101'); UPDATE `vn`.`claimRatio` SET `claimAmount` = '10' WHERE (`clientFk` = '1101');

View File

@ -725,40 +725,40 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
(6, NULL, 57, util.VN_CURDATE(), 5, 7, 'sixth route', 1.7, 60, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 3), (6, NULL, 57, util.VN_CURDATE(), 5, 7, 'sixth route', 1.7, 60, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 3),
(7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5); (7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5);
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`) INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`, `cmrFk`)
VALUES VALUES
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1), (1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 1),
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 1, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2), (2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 1, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2, 2),
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), NULL), (3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), NULL, 3),
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), NULL), (4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), NULL, NULL),
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), NULL), (5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), NULL, NULL),
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), NULL), (6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), NULL, NULL),
(7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(10, 1, 1, 5, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'Ingram Street', 2, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (10, 1, 1, 5, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'Ingram Street', 2, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(11, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (11, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(12, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (12, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(13, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (13, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(14, 1, 2, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1104, 'Malibu Point', 4, NULL, 0, 9, 5, 1, util.VN_CURDATE(), NULL), (14, 1, 2, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1104, 'Malibu Point', 4, NULL, 0, 9, 5, 1, util.VN_CURDATE(), NULL, NULL),
(15, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (15, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(16, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (16, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(17, 1, 7, 2, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL), (17, 1, 7, 2, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL),
(18, 1, 4, 4, 4, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1108, 'Cerebro', 128, NULL, 0, 12, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +12 HOUR), NULL), (18, 1, 4, 4, 4, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1108, 'Cerebro', 128, NULL, 0, 12, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +12 HOUR), NULL, NULL),
(19, 1, 5, 5, NULL, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 1, NULL, 5, 1, util.VN_CURDATE(), NULL), (19, 1, 5, 5, NULL, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 1, NULL, 5, 1, util.VN_CURDATE(), NULL, NULL),
(20, 1, 5, 5, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL), (20, 1, 5, 5, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL, NULL),
(21, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Holland', 102, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL), (21, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Holland', 102, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL, NULL),
(22, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Japan', 103, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL), (22, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Japan', 103, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL, NULL),
(23, NULL, 8, 1, 7, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'address 21', 121, NULL, 0, 5, 5, 1, util.VN_CURDATE(), NULL), (23, NULL, 8, 1, 7, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'address 21', 121, NULL, 0, 5, 5, 1, util.VN_CURDATE(), NULL, NULL),
(24 ,NULL, 8, 1, 7, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 5, 5, 1, util.VN_CURDATE(), NULL), (24 ,NULL, 8, 1, 7, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 5, 5, 1, util.VN_CURDATE(), NULL, NULL),
(25 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (25 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(26 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'An incredibly long alias for testing purposes', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (26 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'An incredibly long alias for testing purposes', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(27 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Wolverine', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (27 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Wolverine', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(28, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (28, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(29, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (29, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL), (31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL),
(32, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL); (32, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL);
INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`)
VALUES VALUES
@ -2405,7 +2405,7 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`)
(14, 'Ticket', 1, 1, 'ticket'), (14, 'Ticket', 1, 1, 'ticket'),
(15, 'Presupuestos', NULL, NULL, 'budgets'), (15, 'Presupuestos', NULL, NULL, 'budgets'),
(16, 'Logistica', NULL, NULL, 'logistics'), (16, 'Logistica', NULL, NULL, 'logistics'),
(17, 'cmr', NULL, NULL, 'cmr'), (17, 'cmr', 1, 1, 'cmr'),
(18, 'dua', NULL, NULL, 'dua'), (18, 'dua', NULL, NULL, 'dua'),
(19, 'inmovilizado', NULL, NULL, 'fixedAssets'), (19, 'inmovilizado', NULL, NULL, 'fixedAssets'),
(20, 'Reclamación', 1, 1, 'claim'); (20, 'Reclamación', 1, 1, 'claim');
@ -3062,3 +3062,8 @@ INSERT INTO `vn`.`clientSms` (`id`, `clientFk`, `smsFk`, `ticketFk`)
(4, 1103, 4, 32), (4, 1103, 4, 32),
(13, 1101, 1, NULL), (13, 1101, 1, NULL),
(14, 1101, 4, 27); (14, 1101, 4, 27);
INSERT INTO `vn`.`cmr` (id,truckPlate,observations,senderInstruccions,paymentInstruccions,specialAgreements,companyFk,addressToFk,addressFromFk,supplierFk,packagesList,merchandiseDetail,state)
VALUES (1,'123456A','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet',442,1,2,1,'Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet'),
(2,'123456N','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet',69,3,4,2,'Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet'),
(3,'123456B','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet',567,5,6,69,'Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet','Lorem ipsum dolor sit amet');

View File

@ -1,16 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `bi`.`nz`(vData DOUBLE)
RETURNS double
DETERMINISTIC
BEGIN
/**
* Devuelve 0, si el parámetro es NULL:
*/
DECLARE vResult DOUBLE;
SET vResult = IFNULL(vData,0);
RETURN vResult;
END$$
DELIMITER ;

View File

@ -130,13 +130,13 @@ BEGIN
-- Calculamos el porcentaje del recobro para añadirlo al precio de venta -- Calculamos el porcentaje del recobro para añadirlo al precio de venta
UPDATE bi.claims_ratio cr UPDATE bi.claims_ratio cr
JOIN ( JOIN (
SELECT Id_Cliente, nz(SUM(Importe)) AS Greuge SELECT Id_Cliente, IFNULL(SUM(Importe), 0) AS Greuge
FROM vn2008.Greuges FROM vn2008.Greuges
WHERE Fecha <= util.VN_CURDATE() WHERE Fecha <= util.VN_CURDATE()
GROUP BY Id_Cliente GROUP BY Id_Cliente
) g ON g.Id_Cliente = cr.Id_Cliente ) g ON g.Id_Cliente = cr.Id_Cliente
SET recobro = GREATEST(0,round(nz(Greuge) / SET recobro = GREATEST(0,round(IFNULL(Greuge, 0) /
(nz(Consumo) * vMonthToRefund / 12 ) ,3)); (IFNULL(Consumo, 0) * vMonthToRefund / 12 ) ,3));
-- Protección neonatos -- Protección neonatos
UPDATE bi.claims_ratio cr UPDATE bi.claims_ratio cr

View File

@ -1,14 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`nz`(vQuantity DOUBLE)
RETURNS double
DETERMINISTIC
BEGIN
DECLARE vResult DOUBLE;
SET vResult = IFNULL(vQuantity,0);
RETURN vResult;
END$$
DELIMITER ;

View File

@ -48,7 +48,7 @@ BEGIN
SELECT lc.companyFk, SELECT lc.companyFk,
c.id, c.id,
0, 0,
- (NZ(lc.credit) - NZ(lc.debit)) - (IFNULL(lc.credit, 0) - IFNULL(lc.debit, 0))
FROM tmp.ledgerComparative lc FROM tmp.ledgerComparative lc
JOIN client c ON c.accountingAccount = lc.account JOIN client c ON c.accountingAccount = lc.account
WHERE lc.`date` BETWEEN vDateFrom AND vDateTo WHERE lc.`date` BETWEEN vDateFrom AND vDateTo

View File

@ -15,11 +15,13 @@ BEGIN
p.code, p.code,
ish.id, ish.id,
s.priority, s.priority,
ish.isChecked ish.isChecked,
ic.url
FROM itemShelving ish FROM itemShelving ish
JOIN item i ON i.id = ish.itemFk JOIN item i ON i.id = ish.itemFk
JOIN shelving s ON vSelf = s.code COLLATE utf8_unicode_ci JOIN shelving s ON vSelf = s.code COLLATE utf8_unicode_ci
LEFT JOIN parking p ON s.parkingFk = p.id LEFT JOIN parking p ON s.parkingFk = p.id
JOIN hedera.imageConfig ic
WHERE ish.shelvingFk COLLATE utf8_unicode_ci = vSelf; WHERE ish.shelvingFk COLLATE utf8_unicode_ci = vSelf;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -60,7 +60,7 @@ BEGIN
SELECT lc.companyFk, SELECT lc.companyFk,
s.id, s.id,
0, 0,
- (NZ(lc.debit) - NZ(lc.credit)) - (IFNULL(lc.debit, 0) - IFNULL(lc.credit, 0))
FROM tmp.ledgerComparative lc FROM tmp.ledgerComparative lc
JOIN supplier s ON s.account = lc.account JOIN supplier s ON s.account = lc.account
WHERE lc.`date` BETWEEN vDateFrom AND vDateTo WHERE lc.`date` BETWEEN vDateFrom AND vDateTo

View File

@ -10,7 +10,7 @@ BEGIN
IF NEW.freightItemFk IS NOT NULL THEN IF NEW.freightItemFk IS NOT NULL THEN
UPDATE ticket SET packages = nz(packages) + 1 WHERE id = NEW.ticketFk; UPDATE ticket SET packages = IFNULL(packages, 0) + 1 WHERE id = NEW.ticketFk;
SELECT IFNULL(MAX(counter),0) +1 INTO intcounter SELECT IFNULL(MAX(counter),0) +1 INTO intcounter
FROM expedition e FROM expedition e

View File

@ -1,14 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn2008`.`nz`(dblCANTIDAD DOUBLE)
RETURNS double
DETERMINISTIC
BEGIN
DECLARE dblRESULT DOUBLE;
SET dblRESULT = IFNULL(dblCANTIDAD,0);
RETURN dblRESULT;
END$$
DELIMITER ;

View File

@ -8,12 +8,6 @@ BEGIN
END; END;
START TRANSACTION; START TRANSACTION;
INSERT INTO vn.clientCredit(clientFk, amount)
SELECT c.id, 0
FROM vn.`client` c
JOIN vn.payMethod pm ON pm.id = c.payMethodFk
WHERE c.credit <> 0 AND pm.`code` = 'card';
UPDATE vn.`client` c UPDATE vn.`client` c
JOIN vn.payMethod pm ON pm.id = c.payMethodFk JOIN vn.payMethod pm ON pm.id = c.payMethodFk
SET credit = 0 SET credit = 0
@ -45,10 +39,6 @@ BEGIN
JOIN clientes_credit USING(Id_Cliente) JOIN clientes_credit USING(Id_Cliente)
SET Clientes.Credito = newCredit; SET Clientes.Credito = newCredit;
INSERT INTO credit(Id_Cliente, amount, Id_Trabajador)
SELECT Id_Cliente, newCredit, NULL
FROM clientes_credit;
DROP TEMPORARY TABLE clientes_credit; DROP TEMPORARY TABLE clientes_credit;
COMMIT; COMMIT;
END$$ END$$

View File

@ -0,0 +1,9 @@
CREATE OR REPLACE DEFINER=`root`@`localhost`
SQL SECURITY DEFINER
VIEW `vn2008`.`credit`
AS SELECT `c`.`id` AS `id`,
`c`.`clientFk` AS `Id_Cliente`,
`c`.`workerFk` AS `Id_Trabajador`,
`c`.`amount` AS `amount`,
`c`.`created` AS `odbc_date`
FROM `vn`.`clientCredit` `c`

View File

@ -1 +0,0 @@
REVOKE EXECUTE ON FUNCTION vn2008.red FROM hrBoss, salesPerson;

View File

@ -0,0 +1,3 @@
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES ('Supplier', 'updateAllFiscalData', 'WRITE', 'ALLOW', 'ROLE', 'administrative'),
('Supplier', 'updateFiscalData', 'WRITE', 'ALLOW', 'ROLE', 'buyer');

View File

@ -1 +1,60 @@
REVOKE EXECUTE ON FUNCTION vn2008.cc_to_iban FROM hr, financial; DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `util`.`accountNumberToIban`(
vAccount VARCHAR(20)
)
RETURNS varchar(4) CHARSET utf8mb3 COLLATE utf8mb3_general_ci
DETERMINISTIC
BEGIN
/**
* Calcula y genera el código IBAN correspondiente
* a un número de cuenta bancaria español.
*
* @param vAccount Número de cuenta bancaria
* @return vIban Código IBAN de 4 caracteres.
*/
DECLARE vIban VARCHAR(4);
SELECT
CONCAT('ES',
RIGHT(
CONCAT(0,
98-MOD(
CONCAT(
MOD(
CONCAT(
MOD(
CONCAT(
MOD(
SUBSTRING(vAccount, 1, 8),
97
),
SUBSTRING(vAccount,9,8)
),
97
),
SUBSTRING(
CONCAT(vAccount, 142800),
17,
8
)
),
97
),
SUBSTRING(
CONCAT(vAccount, 142800),
25,
2
)
),
97
)
),
2
)
) INTO vIban;
RETURN vIban;
END$$
DELIMITER ;
GRANT EXECUTE ON FUNCTION util.accountNumberToIban TO hr, financial;

View File

@ -0,0 +1,34 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`intrastat_estimateNet`(
vSelf INT,
vStems INT
)
RETURNS double
DETERMINISTIC
BEGIN
/**
* Calcula un valor neto estimado en función de
* datos históricos de facturas intrastat.
*
* @param vSelf Id de intrastat
* @param vStems Número de unidades
* @return vNet
*/
DECLARE vNet DOUBLE;
SELECT ROUND(vStems / (SUM(average) / COUNT(average)), 2) INTO vNet
FROM (
SELECT *, stems / net average
FROM invoiceInIntrastat
WHERE intrastatFk = vSelf
AND net
AND stems > 0
ORDER BY dated DESC
LIMIT 20
) sub;
RETURN vNet/2;
END$$
DELIMITER ;
GRANT EXECUTE ON FUNCTION vn.intrastat_estimateNet TO administrative;

View File

@ -0,0 +1,6 @@
CREATE OR REPLACE DEFINER=`root`@`localhost`
SQL SECURITY DEFINER
VIEW `vn2008`.`credit`AS
SELECT 1;
GRANT SELECT ON TABLE vn2008.credit TO financialBoss;

View File

@ -0,0 +1 @@
DROP TABLE vn.timeControlDevice;

View File

@ -18,11 +18,13 @@ services:
memory: 1G memory: 1G
back: back:
image: registry.verdnatura.es/salix-back:${VERSION:?} image: registry.verdnatura.es/salix-back:${VERSION:?}
build: . build:
context: .
dockerfile: back/Dockerfile
environment: environment:
- TZ
- NODE_ENV - NODE_ENV
- DEBUG - DEBUG
- TZ
ports: ports:
- 3000 - 3000
configs: configs:

View File

@ -1,7 +1,9 @@
/* eslint-disable no-console */
require('@babel/register')({presets: ['@babel/env']}); require('@babel/register')({presets: ['@babel/env']});
require('core-js/stable'); require('core-js/stable');
require('regenerator-runtime/runtime'); require('regenerator-runtime/runtime');
require('vn-loopback/server/boot/date')(); require('vn-loopback/server/boot/date')();
const getopts = require('getopts');
const path = require('path'); const path = require('path');
const Myt = require('@verdnatura/myt/myt'); const Myt = require('@verdnatura/myt/myt');
@ -18,12 +20,16 @@ process.on('warning', warning => {
}); });
async function test() { async function test() {
if (process.argv[2] === 'show') const opts = getopts(process.argv.slice(2), {
process.env.E2E_SHOW = true; boolean: ['show']
});
process.env.E2E_SHOW = opts.show;
console.log('Building and running DB container.');
const myt = new Myt(); const myt = new Myt();
await myt.init({workspace: path.join(__dirname, '../..')}); await myt.init({workspace: path.join(__dirname, '../..')});
await myt.run(Run); await myt.run(Run);
await myt.deinit();
const Jasmine = require('jasmine'); const Jasmine = require('jasmine');
const jasmine = new Jasmine(); const jasmine = new Jasmine();
@ -70,12 +76,10 @@ async function test() {
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
await jasmine.execute(); await jasmine.execute();
await myt.deinit();
} }
async function backendStatus() { async function backendStatus() {
log('Awaiting backend connection...'); log('Awaiting backend connection.');
const milliseconds = 1000; const milliseconds = 1000;
const maxAttempts = 10; const maxAttempts = 10;

View File

@ -1,19 +1,19 @@
import 'angular'; import 'angular';
import 'angular-mocks'; import 'angular-mocks';
import core from './front/core/module.js'; import core from './core/module.js';
import './front/salix/components/app/app.js'; import './salix/components/app/app.js';
import './modules/zone/front/module.js'; import '../modules/zone/front/module.js';
import './modules/claim/front/module.js'; import '../modules/claim/front/module.js';
import './modules/client/front/module.js'; import '../modules/client/front/module.js';
import './modules/invoiceOut/front/module.js'; import '../modules/invoiceOut/front/module.js';
import './modules/invoiceIn/front/module.js'; import '../modules/invoiceIn/front/module.js';
import './modules/item/front/module.js'; import '../modules/item/front/module.js';
import './modules/order/front/module.js'; import '../modules/order/front/module.js';
import './modules/route/front/module.js'; import '../modules/route/front/module.js';
import './modules/ticket/front/module.js'; import '../modules/ticket/front/module.js';
import './modules/travel/front/module.js'; import '../modules/travel/front/module.js';
import './modules/worker/front/module.js'; import '../modules/worker/front/module.js';
import './modules/shelving/front/module.js'; import '../modules/shelving/front/module.js';
import 'vn-loopback/server/boot/date'; import 'vn-loopback/server/boot/date';
// Set NODE_ENV // Set NODE_ENV

View File

@ -2,7 +2,6 @@
require('require-yaml'); require('require-yaml');
const gulp = require('gulp'); const gulp = require('gulp');
const PluginError = require('plugin-error'); const PluginError = require('plugin-error');
const argv = require('minimist')(process.argv.slice(2));
const log = require('fancy-log'); const log = require('fancy-log');
const Myt = require('@verdnatura/myt/myt'); const Myt = require('@verdnatura/myt/myt');
const Run = require('@verdnatura/myt/myt-run'); const Run = require('@verdnatura/myt/myt-run');
@ -12,9 +11,6 @@ const Start = require('@verdnatura/myt/myt-start');
let isWindows = /^win/.test(process.platform); let isWindows = /^win/.test(process.platform);
if (argv.NODE_ENV)
process.env.NODE_ENV = argv.NODE_ENV;
let langs = ['es', 'en']; let langs = ['es', 'en'];
let srcDir = './front'; let srcDir = './front';
let modulesDir = './modules'; let modulesDir = './modules';

View File

@ -10,7 +10,7 @@ module.exports = {
}, },
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
setupFilesAfterEnv: [ setupFilesAfterEnv: [
'./jest-front.js' './front/jest-setup.js'
], ],
testMatch: [ testMatch: [
'**/front/**/*.spec.js', '**/front/**/*.spec.js',
@ -37,7 +37,7 @@ module.exports = {
], ],
moduleNameMapper: { moduleNameMapper: {
'\\.(css|scss)$': 'identity-obj-proxy', '\\.(css|scss)$': 'identity-obj-proxy',
'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/fileMock.js', '\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/front/jest-mock.js',
}, },
testURL: 'http://localhost', testURL: 'http://localhost',
verbose: false, verbose: false,

View File

@ -206,6 +206,6 @@
"Incorrect pin": "Incorrect pin.", "Incorrect pin": "Incorrect pin.",
"The notification subscription of this worker cant be modified": "The notification subscription of this worker cant be modified", "The notification subscription of this worker cant be modified": "The notification subscription of this worker cant be modified",
"Name should be uppercase": "Name should be uppercase", "Name should be uppercase": "Name should be uppercase",
"Fecha fuera de rango": "Fecha fuera de rango", "You cannot update these fields": "You cannot update these fields",
"There is no zone for these parameters 34": "There is no zone for these parameters 34" "CountryFK cannot be empty": "Country cannot be empty"
} }

View File

@ -337,7 +337,11 @@
"You already have the mailAlias": "Ya tienes este alias de correo", "You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado", "The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar", "No tickets to invoice": "No hay tickets para facturar",
"This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado",
"Name should be uppercase": "El nombre debe ir en mayúscula", "Name should be uppercase": "El nombre debe ir en mayúscula",
"Bank entity must be specified": "La entidad bancaria es obligatoria", "Bank entity must be specified": "La entidad bancaria es obligatoria",
"An email is necessary": "Es necesario un email" "An email is necessary": "Es necesario un email",
"You cannot update these fields": "No puedes actualizar estos campos",
"CountryFK cannot be empty": "El país no puede estar vacío",
"Cmr file does not exist": "El archivo del cmr no existe"
} }

View File

@ -66,6 +66,7 @@ module.exports = Self => {
cou.country, cou.country,
c.id clientId, c.id clientId,
c.socialName clientSocialName, c.socialName clientSocialName,
u.nickname workerSocialName,
SUM(s.quantity * s.price * ( 100 - s.discount ) / 100) amount, SUM(s.quantity * s.price * ( 100 - s.discount ) / 100) amount,
negativeBase.taxableBase, negativeBase.taxableBase,
negativeBase.ticketFk, negativeBase.ticketFk,
@ -80,6 +81,7 @@ module.exports = Self => {
JOIN vn.client c ON c.id = t.clientFk JOIN vn.client c ON c.id = t.clientFk
JOIN vn.country cou ON cou.id = c.countryFk JOIN vn.country cou ON cou.id = c.countryFk
LEFT JOIN vn.worker w ON w.id = c.salesPersonFk LEFT JOIN vn.worker w ON w.id = c.salesPersonFk
JOIN account.user u ON u.id = w.id
LEFT JOIN ( LEFT JOIN (
SELECT ticketFk, taxableBase SELECT ticketFk, taxableBase
FROM tmp.ticketAmount FROM tmp.ticketAmount

View File

@ -16,7 +16,7 @@ class Controller extends Section {
this.filter = { this.filter = {
where: { where: {
itemFk: this.$params.id, itemFk: this.$params.id,
shipped: { landed: {
between: [from, to] between: [from, to]
} }
} }
@ -36,7 +36,7 @@ class Controller extends Section {
const to = new Date(this._dateTo); const to = new Date(this._dateTo);
to.setHours(23, 59, 59, 59); to.setHours(23, 59, 59, 59);
this.filter.where.shipped = { this.filter.where.landed = {
between: [from, to] between: [from, to]
}; };
this.$.model.refresh(); this.$.model.refresh();
@ -53,7 +53,7 @@ class Controller extends Section {
const to = new Date(value); const to = new Date(value);
to.setHours(23, 59, 59, 59); to.setHours(23, 59, 59, 59);
this.filter.where.shipped = { this.filter.where.landed = {
between: [from, to] between: [from, to]
}; };
this.$.model.refresh(); this.$.model.refresh();

View File

@ -0,0 +1,89 @@
const {Email} = require('vn-print');
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('cmrEmail', {
description: 'Sends the email with an cmr attached PDF',
accessType: 'WRITE',
accepts: [
{
arg: 'tickets',
type: ['number'],
required: true,
description: 'The ticket id',
}
],
http: {
path: '/cmrEmail',
verb: 'POST'
}
});
Self.cmrEmail = async function(ctx, tickets, options) {
const models = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
for (const ticketId of tickets) {
const ticket = await models.Ticket.findOne({
where: {
id: ticketId
},
include: [{
relation: 'client',
fields: ['email']
}]
}, myOptions);
const recipient = ticket.client().email;
if (!recipient)
throw new UserError('There is no assigned email for this client');
const dms = await models.TicketDms.findOne({
where: {ticketFk: ticketId},
include: [{
relation: 'dms',
fields: ['id'],
scope: {
relation: 'dmsType',
scope: {
where: {code: 'cmr'}
}
}
}]
}, myOptions);
if (!dms) throw new UserError('Cmr file does not exist');
const response = await models.Dms.downloadFile(ctx, dms.id);
const email = new Email('cmr', {
ticketId,
lang: ctx.req.getLocale(),
recipient
});
await email.send({
overrideAttachments: true,
attachments: [{
filename: `${ticket.cmrFk}.pdf`,
content: response[0]
}]
});
}
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -1,6 +1,4 @@
const JSZip = require('jszip'); const JSZip = require('jszip');
const axios = require('axios');
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('downloadCmrsZip', { Self.remoteMethodCtx('downloadCmrsZip', {
@ -37,35 +35,20 @@ module.exports = Self => {
Self.downloadCmrsZip = async function(ctx, ids, options) { Self.downloadCmrsZip = async function(ctx, ids, options) {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {}; const myOptions = {};
const token = ctx.req.accessToken;
const zip = new JSZip(); const zip = new JSZip();
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const zipConfig = await models.ZipConfig.findOne(null, myOptions);
let totalSize = 0;
ids = ids.split(','); ids = ids.split(',');
try {
for (let id of ids) {
if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large');
const response = await axios.get(
`${ctx.req.headers.referer}api/Routes/${id}/cmr?access_token=${token.id}`, {
...myOptions,
responseType: 'arraybuffer',
});
if (response.headers['content-type'] !== 'application/pdf') for (const id of ids) {
throw new UserError(`The response is not a PDF`); ctx.args = ctx.args || {};
ctx.args.id = Number(id);
zip.file(`${id}.pdf`, response.data, { binary: true }); const [data] = await models.Route.cmr(ctx, myOptions);
zip.file(`${id}.pdf`, data, {binary: true});
} }
const zipStream = zip.generateNodeStream({streamFiles: true}); const zipStream = zip.generateNodeStream({streamFiles: true});
return [zipStream, 'application/zip', `filename="cmrs.zip"`]; return [zipStream, 'application/zip', `filename="cmrs.zip"`];
} catch (e) {
throw e;
}
}; };
}; };

View File

@ -1,3 +1,4 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('driverRouteEmail', { Self.remoteMethodCtx('driverRouteEmail', {
description: 'Sends the driver route email with an attached PDF', description: 'Sends the driver route email with an attached PDF',
@ -9,24 +10,14 @@ module.exports = Self => {
required: true, required: true,
description: 'The client id', description: 'The client id',
http: {source: 'path'} http: {source: 'path'}
}, }, {
{
arg: 'recipient',
type: 'string',
description: 'The recipient email',
required: true,
},
{
arg: 'replyTo', arg: 'replyTo',
type: 'string', type: 'string',
description: 'The sender email to reply to', description: 'The sender email to reply to',
required: false }, {
},
{
arg: 'recipientId', arg: 'recipientId',
type: 'number', type: 'number',
description: 'The recipient id to send to the recipient preferred language', description: 'The recipient id to send to the recipient preferred language',
required: false
} }
], ],
returns: { returns: {
@ -39,5 +30,28 @@ module.exports = Self => {
} }
}); });
Self.driverRouteEmail = ctx => Self.sendTemplate(ctx, 'driver-route'); Self.driverRouteEmail = async(ctx, id) => {
const models = Self.app.models;
const {workerFk, agencyMode} = await Self.findById(id, {
fields: ['workerFk', 'agencyModeFk'],
include: {relation: 'agencyMode'}
});
const {reportMail} = agencyMode();
let user;
let account;
if (workerFk) {
user = await models.VnUser.findById(workerFk, {
fields: ['active', 'id'],
include: {relation: 'emailUser'}
});
account = await models.Account.findById(workerFk);
}
if (user?.active && account) ctx.args.recipient = user.emailUser().email;
else ctx.args.recipient = reportMail;
if (!ctx.args.recipient) throw new UserError('An email is necessary');
return Self.sendTemplate(ctx, 'driver-route');
};
}; };

View File

@ -1,5 +1,5 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const {ParameterizedSQL} = require('loopback-connector');
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('getTickets', { Self.remoteMethod('getTickets', {
@ -83,13 +83,15 @@ module.exports = Self => {
const where = filter.where; const where = filter.where;
where['r.id'] = filter.id; where['r.id'] = filter.id;
where.and = [{or: [
{'t.packages': {gt: 0}},
{and: [{'ot.code': 'delivery'}, {'tob.observationTypeFk': {neq: null}}]}
]}];
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
stmt.merge(conn.makeGroupBy('t.id')); stmt.merge(conn.makeGroupBy('t.id'));
stmt.merge(conn.makeOrderBy(filter.order)); stmt.merge(conn.makeOrderBy(filter.order));
const tickets = await conn.executeStmt(stmt, myOptions); return conn.executeStmt(stmt, myOptions);
return tickets;
}; };
}; };

View File

@ -0,0 +1,25 @@
const models = require('vn-loopback/server/server').models;
describe('route downloadCmrsZip()', () => {
it('should create a zip file with the given cmr ids', async() => {
const tx = await models.Route.beginTransaction({});
const ctx = {
req: {
getLocale: () => {
return 'en';
},
accessToken: {userId: 9}
}
};
let cmrs = '1,2';
try {
const stream = await models.Route.downloadCmrsZip(ctx, cmrs);
expect(stream[0]).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,58 @@
{
"name": "Cmr",
"base": "VnModel",
"options": {
"mysql": {
"table": "cmr"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"truckPlate": {
"type": "number"
},
"observations": {
"type": "string"
},
"senderInstrucctions": {
"type": "string"
},
"paymentInstruccions": {
"type": "string"
},
"specialAgreements": {
"type": "string"
},
"created": {
"type": "date"
},
"companyFk": {
"type": "number"
},
"addressToFk": {
"type": "number"
},
"addressFromFk": {
"type": "number"
},
"supplierFk": {
"type": "number"
},
"packagesList": {
"type": "string"
},
"merchandiseDetail": {
"type": "string"
},
"landed": {
"type": "date"
},
"ead": {
"type": "date"
}
}
}

View File

@ -17,6 +17,7 @@ module.exports = Self => {
require('../methods/route/cmr')(Self); require('../methods/route/cmr')(Self);
require('../methods/route/getExternalCmrs')(Self); require('../methods/route/getExternalCmrs')(Self);
require('../methods/route/downloadCmrsZip')(Self); require('../methods/route/downloadCmrsZip')(Self);
require('../methods/route/cmrEmail')(Self);
require('../methods/route/getExpeditionSummary')(Self); require('../methods/route/getExpeditionSummary')(Self);
require('../methods/route/getByWorker')(Self); require('../methods/route/getByWorker')(Self);

View File

@ -1,92 +1,142 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('Supplier updateFiscalData', () => { describe('Supplier updateFiscalData()', () => {
const supplierId = 1; const supplierId = 1;
const administrativeId = 5; const administrativeId = 5;
const employeeId = 1; const buyerId = 35;
const defaultData = {
name: 'PLANTS SL', const name = 'NEW PLANTS';
nif: '06089160W', const city = 'PONTEVEDRA';
account: '4100000001', const nif = 'A68446004';
sageTaxTypeFk: 4, const account = '4000000005';
sageWithholdingFk: 1, const sageTaxTypeFk = 5;
sageTransactionTypeFk: 1, const sageWithholdingFk = 2;
postCode: '15214', const sageTransactionTypeFk = 2;
city: 'PONTEVEDRA', const postCode = '46460';
provinceFk: 1, const phone = 456129367;
countryFk: 1, const street = ' Fake address 12 3 flat';
const provinceFk = 2;
const countryFk = 1;
const supplierActivityFk = 'animals';
const healthRegister = '400664487H';
let ctx;
let options;
let tx;
beforeEach(async() => {
ctx = {
req: {
accessToken: {userId: buyerId},
headers: {origin: 'http://localhost'},
__: value => value
},
args: {}
}; };
it('should return an error if the user is not administrative', async() => {
const ctx = {req: {accessToken: {userId: employeeId}}};
ctx.args = {};
let error;
await app.models.Supplier.updateFiscalData(ctx, supplierId)
.catch(e => {
error = e;
});
expect(error.message).toBeDefined();
});
it('should check that the supplier fiscal data is untainted', async() => {
const supplier = await app.models.Supplier.findById(supplierId);
expect(supplier.name).toEqual(defaultData.name);
expect(supplier.nif).toEqual(defaultData.nif);
expect(supplier.account).toEqual(defaultData.account);
expect(supplier.sageTaxTypeFk).toEqual(defaultData.sageTaxTypeFk);
expect(supplier.sageWithholdingFk).toEqual(defaultData.sageWithholdingFk);
expect(supplier.sageTransactionTypeFk).toEqual(defaultData.sageTransactionTypeFk);
expect(supplier.postCode).toEqual(defaultData.postCode);
expect(supplier.city).toEqual(defaultData.city);
expect(supplier.provinceFk).toEqual(defaultData.provinceFk);
expect(supplier.countryFk).toEqual(defaultData.countryFk);
});
it('should update the supplier fiscal data and return the count if changes made', async() => {
const activeCtx = {
accessToken: {userId: administrativeId},
};
const ctx = {req: activeCtx};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: ctx.req
}); });
ctx.args = { options = {transaction: tx};
name: 'WEAPON DEALER', tx = await models.Sale.beginTransaction({});
nif: 'A68446004', options.transaction = tx;
account: '4000000005', });
sageTaxTypeFk: 5,
sageWithholdingFk: 2,
sageTransactionTypeFk: 2,
postCode: '46460',
city: 'VALENCIA',
provinceFk: 2,
countryFk: 1,
supplierActivityFk: 'animals',
healthRegister: '400664487H'
};
const result = await app.models.Supplier.updateFiscalData(ctx, supplierId); afterEach(async() => {
await tx.rollback();
});
expect(result.name).toEqual('WEAPON DEALER'); it('should throw an error if it is a buyer and tries to update forbidden fiscal data', async() => {
expect(result.nif).toEqual('A68446004'); try {
expect(result.account).toEqual('4000000005'); await models.Supplier.updateFiscalData(ctx,
expect(result.sageTaxTypeFk).toEqual(5); supplierId,
expect(result.sageWithholdingFk).toEqual(2); name,
expect(result.sageTransactionTypeFk).toEqual(2); nif,
expect(result.postCode).toEqual('46460'); account,
expect(result.city).toEqual('VALENCIA'); undefined,
expect(result.provinceFk).toEqual(2); sageTaxTypeFk,
expect(result.countryFk).toEqual(1); undefined,
expect(result.supplierActivityFk).toEqual('animals'); sageTransactionTypeFk,
expect(result.healthRegister).toEqual('400664487H'); undefined,
undefined,
undefined,
provinceFk,
countryFk,
supplierActivityFk,
healthRegister,
undefined,
undefined,
options);
} catch (e) {
expect(e.message).toEqual('You cannot update these fields');
}
});
// Restores it('should update the granted fiscal data if it is a buyer', async() => {
ctx.args = defaultData; const supplier = await models.Supplier.updateFiscalData(ctx,
await app.models.Supplier.updateFiscalData(ctx, supplierId); supplierId,
undefined,
undefined,
account,
phone,
undefined,
undefined,
undefined,
postCode,
street,
city,
provinceFk,
undefined,
undefined,
undefined,
undefined,
undefined,
options);
expect(supplier.account).toEqual(account);
expect(supplier.phone).toEqual(phone);
expect(supplier.postCode).toEqual(postCode);
expect(supplier.account).toEqual(account);
expect(supplier.city).toEqual(city);
expect(supplier.provinceFk).toEqual(provinceFk);
});
it('should update all fiscalData if it is an administative', async() => {
const supplier = await models.Supplier.updateFiscalData(ctx,
supplierId,
name,
nif,
account,
phone,
sageTaxTypeFk,
sageWithholdingFk,
sageTransactionTypeFk,
postCode,
street,
city,
provinceFk,
countryFk,
supplierActivityFk,
healthRegister,
undefined,
undefined,
options);
expect(supplier.name).toEqual(name);
expect(supplier.nif).toEqual(nif);
expect(supplier.account).toEqual(account);
expect(supplier.phone).toEqual(phone);
expect(supplier.sageTaxTypeFk).toEqual(sageTaxTypeFk);
expect(supplier.sageWithholdingFk).toEqual(sageWithholdingFk);
expect(supplier.sageTransactionTypeFk).toEqual(sageTransactionTypeFk);
expect(supplier.postCode).toEqual(postCode);
expect(supplier.street).toEqual(street);
expect(supplier.city).toEqual(city);
expect(supplier.provinceFk).toEqual(provinceFk);
expect(supplier.countryFk).toEqual(countryFk);
expect(supplier.supplierActivityFk).toEqual(supplierActivityFk);
expect(supplier.healthRegister).toEqual(healthRegister);
}); });
}); });

View File

@ -1,75 +1,59 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('updateFiscalData', { Self.remoteMethodCtx('updateFiscalData', {
description: 'Updates fiscal data of a supplier', description: 'Updates fiscal data of a supplier',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'ctx',
type: 'Object',
http: {source: 'context'}
},
{
arg: 'id', arg: 'id',
type: 'Number', type: 'Number',
description: 'The supplier id', description: 'The supplier id',
http: {source: 'path'} http: {source: 'path'}
}, }, {
{
arg: 'name', arg: 'name',
type: 'string' type: 'string'
}, }, {
{
arg: 'nif', arg: 'nif',
type: 'string' type: 'string'
}, }, {
{
arg: 'account', arg: 'account',
type: 'any' type: 'any'
}, }, {
{ arg: 'phone',
type: 'string'
}, {
arg: 'sageTaxTypeFk', arg: 'sageTaxTypeFk',
type: 'any' type: 'any'
}, }, {
{
arg: 'sageWithholdingFk', arg: 'sageWithholdingFk',
type: 'any' type: 'any'
}, }, {
{
arg: 'sageTransactionTypeFk', arg: 'sageTransactionTypeFk',
type: 'any' type: 'any'
}, }, {
{
arg: 'postCode', arg: 'postCode',
type: 'any' type: 'any'
}, }, {
{
arg: 'street', arg: 'street',
type: 'any' type: 'any'
}, }, {
{
arg: 'city', arg: 'city',
type: 'string' type: 'string'
}, }, {
{
arg: 'provinceFk', arg: 'provinceFk',
type: 'any' type: 'any'
}, }, {
{
arg: 'countryFk', arg: 'countryFk',
type: 'any' type: 'any'
}, }, {
{
arg: 'supplierActivityFk', arg: 'supplierActivityFk',
type: 'string' type: 'string'
}, }, {
{
arg: 'healthRegister', arg: 'healthRegister',
type: 'string' type: 'string'
}, }, {
{
arg: 'isVies', arg: 'isVies',
type: 'boolean' type: 'boolean'
}, }, {
{
arg: 'isTrucker', arg: 'isTrucker',
type: 'boolean' type: 'boolean'
}], }],
@ -84,15 +68,42 @@ module.exports = Self => {
} }
}); });
Self.updateFiscalData = async(ctx, supplierId) => { Self.updateFiscalData = async(ctx, supplierId, name, nif, account, phone, sageTaxTypeFk, sageWithholdingFk, sageTransactionTypeFk, postCode, street, city, provinceFk, countryFk, supplierActivityFk, healthRegister, isVies, isTrucker, options) => {
const models = Self.app.models; const models = Self.app.models;
const args = ctx.args; const {args} = ctx;
const myOptions = {};
const supplier = await models.Supplier.findById(supplierId); const supplier = await models.Supplier.findById(supplierId);
// Remove unwanted properties if (typeof options == 'object') Object.assign(myOptions, options);
delete args.ctx; delete args.ctx;
delete args.id; delete args.id;
return supplier.updateAttributes(args); const updateAllFiscalData = await models.ACL.checkAccessAcl(ctx, 'Supplier', 'updateAllFiscalData', 'WRITE');
if (!updateAllFiscalData) {
for (const arg in args) {
if (args[arg] && !['street', 'postCode', 'city', 'provinceFk', 'phone'].includes(arg))
throw new UserError('You cannot update these fields');
}
}
return supplier.updateAttributes({
name,
nif,
account,
phone,
sageTaxTypeFk,
sageWithholdingFk,
sageTransactionTypeFk,
postCode,
street,
city,
provinceFk,
countryFk,
supplierActivityFk,
healthRegister,
isVies,
isTrucker
}, myOptions);
}; };
}; };

View File

@ -0,0 +1,86 @@
const {Readable} = require('stream');
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('saveCmr', {
description: 'Save cmr',
accessType: 'WRITE',
accepts: [
{
arg: 'tickets',
type: ['number'],
required: true,
description: 'The tickets'
}
],
http: {
path: `/saveCmr`,
verb: 'POST'
}
});
Self.saveCmr = async(ctx, tickets, options) => {
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const dmsTypeCmr = await models.DmsType.findOne({
where: {code: 'cmr'},
fields: ['id']
}, myOptions);
for (const ticketId of tickets) {
const ticket = await models.Ticket.findById(ticketId, myOptions);
if (ticket.cmrFk) {
const hasDmsCmr = await models.TicketDms.findOne({
where: {ticketFk: ticketId},
include: {
relation: 'dms',
fields: ['dmsFk'],
scope: {
where: {dmsTypeFk: dmsTypeCmr.id}
}
}
}, myOptions);
if (hasDmsCmr?.dms())
throw new UserError('This ticket already has a cmr saved');
ctx.args.id = ticket.cmrFk;
const response = await models.Route.cmr(ctx, myOptions);
const pdfStream = Readable.from(Buffer.from(response[0]));
const data = {
workerFk: ctx.req.accessToken.userId,
dmsTypeFk: dmsTypeCmr.id,
companyFk: ticket.companyFk,
warehouseFk: ticket.warehouseFk,
reference: ticket.id,
contentType: 'application/pdf',
hasFile: true
};
const dms = await models.Dms.createFromStream(data, 'pdf', pdfStream, myOptions);
await models.TicketDms.create({
ticketFk: ticketId,
dmsFk: dms.id
}, myOptions);
}
}
if (tx) await tx.commit();
return;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -33,8 +33,8 @@ module.exports = Self => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId}; const myOptions = {userId: ctx.req.accessToken.userId};
let tx; let tx;
let dms; let ticket;
let gestDocCreated = false; let externalTickets = [];
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
@ -44,6 +44,11 @@ module.exports = Self => {
myOptions.transaction = tx; myOptions.transaction = tx;
} }
const dmsTypeTicket = await models.DmsType.findOne({
where: {code: 'ticket'},
fields: ['id']
}, myOptions);
async function setLocation(ticketId) { async function setLocation(ticketId) {
await models.Delivery.create({ await models.Delivery.create({
ticketFk: ticketId, ticketFk: ticketId,
@ -53,102 +58,106 @@ module.exports = Self => {
}, myOptions); }, myOptions);
} }
async function gestDocExists(ticketId) { async function hasSignDms(ticketId) {
const ticketDms = await models.TicketDms.findOne({ const ticketDms = await models.TicketDms.findOne({
where: {ticketFk: ticketId}, where: {ticketFk: ticketId},
fields: ['dmsFk']
}, myOptions);
if (!ticketDms) return false;
const ticket = await models.Ticket.findById(ticketId, {fields: ['isSigned']}, myOptions);
if (ticket.isSigned == true)
return true;
else
await models.Dms.destroyAll({where: {reference: ticketId}}, myOptions);
return false;
}
async function createGestDoc(id) {
const ticket = await models.Ticket.findById(id,
{
include: [ include: [
{ {
relation: 'warehouse', relation: 'dms',
fields: ['id'],
scope: { scope: {
fields: ['id'] where: {dmsTypeFk: dmsTypeTicket.id}
} }
}, { }
relation: 'client', ]
}, myOptions);
if (ticketDms?.dms()?.id) return true;
}
async function createGestDoc() {
const ctxUploadFile = Object.assign({}, ctx);
ctxUploadFile.args = {
warehouseId: ticket.warehouseFk,
companyId: ticket.companyFk,
dmsTypeId: dmsTypeTicket.id,
reference: ticket.id,
description: `Firma del cliente - Ruta ${ticket.route().id}`,
contentType: 'image/png',
hasFile: true
};
const dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
await models.TicketDms.create({ticketFk: ticket.id, dmsFk: dms[0].id}, myOptions);
}
try {
for (const ticketId of tickets) {
ticket = await models.Ticket.findById(ticketId, {
include: [{
relation: 'address',
scope: { scope: {
fields: ['name'] include: {
relation: 'province',
scope: {
include: {
relation: 'country',
scope: {
fields: ['code']
}
}
}
}
} }
}, { }, {
relation: 'route', relation: 'route',
scope: { scope: {
fields: ['id'] fields: ['id']
} }
} }]
]
}, myOptions); }, myOptions);
const dmsType = await models.DmsType.findOne({where: {code: 'Ticket'}, fields: ['id']}, myOptions);
const ctxUploadFile = Object.assign({}, ctx);
if (ticket.route() === null)
throw new UserError('Ticket without route');
ctxUploadFile.args = {
warehouseId: ticket.warehouseFk,
companyId: ticket.companyFk,
dmsTypeId: dmsType.id,
reference: '',
description: `Firma del cliente - Ruta ${ticket.route().id}`,
hasFile: false
};
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
gestDocCreated = true;
}
try { const ticketState = await models.TicketState.findOne({
for (const ticketId of tickets) { where: {ticketFk: ticketId},
const ticketState = await models.TicketState.findOne(
{where: {ticketFk: ticketId},
fields: ['alertLevel'] fields: ['alertLevel']
}, myOptions); }, myOptions);
const packedAlertLevel = await models.AlertLevel.findOne({where: {code: 'PACKED'}, const packedAlertLevel = await models.AlertLevel.findOne({
where: {code: 'PACKED'},
fields: ['id'] fields: ['id']
}, myOptions); }, myOptions);
if (!ticketState) if (!ticketState)
throw new UserError('Ticket does not exist'); throw new UserError('Ticket does not exist');
if (!ticket.route())
throw new UserError('Ticket without route');
if (ticketState.alertLevel < packedAlertLevel.id) if (ticketState.alertLevel < packedAlertLevel.id)
throw new UserError('This ticket cannot be signed because it has not been boxed'); throw new UserError('This ticket cannot be signed because it has not been boxed');
if (await gestDocExists(ticketId)) if (await ticket.isSigned)
throw new UserError('Ticket is already signed'); throw new UserError('Ticket is already signed');
if (location) await setLocation(ticketId); if (location) await setLocation(ticketId);
if (!gestDocCreated) await createGestDoc(ticketId); if (!await hasSignDms(ticketId))
await models.TicketDms.create({ticketFk: ticketId, dmsFk: dms[0].id}, myOptions); await createGestDoc(ticketId);
const ticket = await models.Ticket.findById(ticketId, null, myOptions);
await ticket.updateAttribute('isSigned', true, myOptions); await ticket.updateAttribute('isSigned', true, myOptions);
const deliveryState = await models.State.findOne({ const deliveryState = await models.State.findOne({
where: { where: {code: 'DELIVERED'}
code: 'DELIVERED'
}
}, myOptions); }, myOptions);
await models.Ticket.state(ctx, { await models.Ticket.state(ctx, {
ticketFk: ticketId, ticketFk: ticketId,
stateFk: deliveryState.id stateFk: deliveryState.id
}, myOptions); }, myOptions);
}
if (ticket?.address()?.province()?.country()?.code != 'ES') {
await models.Ticket.saveCmr(ctx, [ticketId], myOptions);
externalTickets.push(ticketId);
}
}
if (tx) await tx.commit(); if (tx) await tx.commit();
return;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
throw e; throw e;
} }
await models.Route.cmrEmail(ctx, externalTickets);
}; };
}; };

View File

@ -0,0 +1,41 @@
const models = require('vn-loopback/server/server').models;
describe('ticket saveCmr()', () => {
it(`should save cmr`, async() => {
const tx = await models.Ticket.beginTransaction({});
const ctx = {
req: {
getLocale: () => {
return 'en';
},
accessToken: {userId: 9}
},
args: {}
};
try {
const options = {transaction: tx};
const ticket = [2];
await models.Ticket.saveCmr(ctx, ticket, options);
const hasDmsCmr = await models.TicketDms.findOne({
where: {ticketFk: ticket[0]},
include: [{
relation: 'dms',
fields: ['id'],
scope: {
relation: 'dmsType',
scope: {
where: {code: 'cmr'}
}
}
}]
}, options);
expect(hasDmsCmr?.dms()?.id).toBeGreaterThanOrEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -1,14 +1,11 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('Ticket saveSign()', () => { describe('Ticket saveSign()', () => {
const FormData = require('form-data');
const data = new FormData();
let ctx = {req: { let ctx = {req: {
accessToken: {userId: 9}, getLocale: () => {
headers: { return 'en';
...data.getHeaders() },
} accessToken: {userId: 9}
}}; }};
it(`should throw error if the ticket's alert level is lower than 2`, async() => { it(`should throw error if the ticket's alert level is lower than 2`, async() => {
@ -17,9 +14,9 @@ describe('Ticket saveSign()', () => {
let error; let error;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
ctx.args = {tickets: [ticketWithOkState]}; const tickets = [ticketWithOkState];
await models.Ticket.saveSign(ctx, options); await models.Ticket.saveSign(ctx, tickets, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -41,6 +41,7 @@ module.exports = function(Self) {
require('../methods/ticket/collectionLabel')(Self); require('../methods/ticket/collectionLabel')(Self);
require('../methods/ticket/expeditionPalletLabel')(Self); require('../methods/ticket/expeditionPalletLabel')(Self);
require('../methods/ticket/saveSign')(Self); require('../methods/ticket/saveSign')(Self);
require('../methods/ticket/saveCmr')(Self);
require('../methods/ticket/invoiceTickets')(Self); require('../methods/ticket/invoiceTickets')(Self);
require('../methods/ticket/invoiceTicketsAndPdf')(Self); require('../methods/ticket/invoiceTicketsAndPdf')(Self);
require('../methods/ticket/docuwareDownload')(Self); require('../methods/ticket/docuwareDownload')(Self);

View File

@ -66,6 +66,9 @@
}, },
"weight": { "weight": {
"type": "number" "type": "number"
},
"cmrFk": {
"type": "number"
} }
}, },
"relations": { "relations": {
@ -139,6 +142,11 @@
"type": "belongsTo", "type": "belongsTo",
"model": "Zone", "model": "Zone",
"foreignKey": "zoneFk" "foreignKey": "zoneFk"
},
"cmrFk": {
"type": "belongsTo",
"model": "Cmr",
"foreignKey": "cmrFk"
} }
} }
} }

View File

@ -90,7 +90,6 @@
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"merge-stream": "^1.0.1", "merge-stream": "^1.0.1",
"minimist": "^1.2.5",
"node-sass": "^9.0.0", "node-sass": "^9.0.0",
"nodemon": "^2.0.16", "nodemon": "^2.0.16",
"plugin-error": "^1.0.1", "plugin-error": "^1.0.1",

View File

@ -232,9 +232,6 @@ devDependencies:
merge-stream: merge-stream:
specifier: ^1.0.1 specifier: ^1.0.1
version: 1.0.1 version: 1.0.1
minimist:
specifier: ^1.2.5
version: 1.2.8
node-sass: node-sass:
specifier: ^9.0.0 specifier: ^9.0.0
version: 9.0.0 version: 9.0.0

View File

@ -35,7 +35,8 @@ module.exports = {
logger.error(`[Print] => ${err.message}`); logger.error(`[Print] => ${err.message}`);
}); });
cluster.on('queue', () => logger.info('Printing task initialized by pool')); cluster.on('queue', () =>
process.env.SPEC_IS_RUNNING === 'false' && logger.info('Printing task initialized by pool'));
}); });
} }
}; };

View File

@ -0,0 +1,11 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/email.css`])
.mergeStyles();

View File

@ -0,0 +1,6 @@
[
{
"filename": "cmr.pdf",
"component": "cmr"
}
]

View File

@ -0,0 +1,12 @@
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p>{{$t('dear')}},</p>
<p v-html="$t('description', [cmr.id, cmr.ticketFk])"></p>
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div>
</div>
</email-body>

View File

@ -0,0 +1,22 @@
const Component = require(`vn-print/core/component`);
const emailBody = new Component('email-body');
module.exports = {
name: 'cmr',
async serverPrefetch() {
this.cmr = await this.fetchCmr(this.ticketId);
},
methods: {
fetchCmr(ticketId) {
return this.findOneFromDef('cmr', [ticketId]);
},
},
components: {
'email-body': emailBody.build(),
},
props: {
ticketId: {
type: Number,
required: true
}
}
};

View File

@ -0,0 +1,9 @@
subject: Your CMR
title: Your CMR
dear: Dear Customer
description: The CMR <strong>{0}</strong> corresponding to order <strong>{1}</strong> is now available. <br/>
You can download it by clicking on the attachment in this email.
poll: If you wish, you can respond to our satisfaction survey to
help us provide better service. Your opinion is very important to us!
help: If you have any doubts, do not hesitate to ask, <strong>we are here to serve you!</strong>
conclusion: Thank you for your attention!

View File

@ -0,0 +1,9 @@
subject: Tu CMR
title: Tu CMR
dear: Estimado cliente
description: Ya está disponible el CMR <strong>{0}</strong> correspondiente al pedido <strong>{1}</strong>. <br/>
Puedes descargarla haciendo clic en el adjunto de este correo.
poll: Si lo deseas, puedes responder a nuestra encuesta de satisfacción para
ayudarnos a prestar un mejor servicio. ¡Tu opinión es muy importante para nosotros!
help: Cualquier duda que te surja, no dudes en consultarla, <strong>¡estamos para atenderte!</strong>
conclusion: ¡Gracias por tu atención!

View File

@ -0,0 +1,9 @@
subject: Votre CMR
title: Votre CMR
dear: Cher client
description: Le CMR <strong>{0}</strong> correspondant à la commande <strong>{1}</strong> est maintenant disponible. <br/>
Vous pouvez le télécharger en cliquant sur la pièce jointe de cet e-mail.
poll: Si vous le souhaitez, vous pouvez répondre à notre enquête de satisfaction pour
nous aider à améliorer notre service. Votre avis est très important pour nous !
help: Si vous avez des doutes, n'hésitez pas à nous consulter, <strong>nous sommes là pour vous servir !</strong>
conclusion: Merci de votre attention !

View File

@ -0,0 +1,9 @@
subject: Seu CMR
title: Seu CMR
dear: Caro cliente
description: O CMR <strong>{0}</strong> correspondente ao pedido <strong>{1}</strong> já está disponível. <br/>
Você pode baixá-lo clicando no anexo deste e-mail.
poll: Se desejar, pode responder à nossa pesquisa de satisfação para
nos ajudar a oferecer um serviço melhor. Sua opinião é muito importante para nós!
help: Se tiver alguma dúvida, não hesite em nos consultar, <strong>estamos aqui para atendê-lo!</strong>
conclusion: Obrigado pela sua atenção!

View File

@ -0,0 +1,5 @@
SELECT t.id ticketFk,
c.id
FROM ticket t
JOIN cmr c ON c.id = t.cmrFk
WHERE t.id = ?