Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5509-entryDmsBack

This commit is contained in:
Alex Moreno 2024-02-13 08:13:58 +01:00
commit 06fe4957d5
61 changed files with 1365 additions and 780 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

@ -115,6 +115,7 @@ async function test() {
// 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

@ -184,8 +184,8 @@ INSERT INTO `vn`.`warehouse`(`id`, `name`, `code`, `isComparative`, `isInventory
(13, 'Inventory', 'inv', 1, 1, 1, 0, 0, 0, 1, 0, 0, 0), (13, 'Inventory', 'inv', 1, 1, 1, 0, 0, 0, 1, 0, 0, 0),
(60, 'Algemesi', NULL, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0); (60, 'Algemesi', NULL, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0);
INSERT INTO `vn`.`sectorType` (id,description) INSERT INTO `vn`.`sectorType` (`id`, `code`)
VALUES (1,'First type'); VALUES (1,'normal');
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `code`, `typeFk`) INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `code`, `typeFk`)
VALUES VALUES
@ -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'),
@ -3068,3 +3068,8 @@ INSERT INTO `vn`.`clientSms` (`id`, `clientFk`, `smsFk`, `ticketFk`)
INSERT INTO `vn`.`entryDms`(`entryFk`, `dmsFk`, `editorFk`) INSERT INTO `vn`.`entryDms`(`entryFk`, `dmsFk`, `editorFk`)
VALUES VALUES
(1, 9, 9); (1, 9, 9);
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

@ -0,0 +1,58 @@
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 ;

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

@ -70,9 +70,9 @@ BEGIN
ish.created, ish.created,
ish.visible, ish.visible,
IFNULL( IFNULL(
IF(st.description = 'previousByPacking', ish.packing, g.`grouping`), IF(st.code = 'previousByPacking', ish.packing, g.`grouping`),
1) `grouping`, 1) `grouping`,
st.description = 'previousPrepared' isPreviousPrepared, st.code = 'previousPrepared' isPreviousPrepared,
iss.id itemShelvingSaleFk, iss.id itemShelvingSaleFk,
ts.ticketFk, ts.ticketFk,
iss.id, iss.id,

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

@ -207,7 +207,7 @@ proc: BEGIN
ENGINE = MEMORY ENGINE = MEMORY
SELECT ish.itemFk, SELECT ish.itemFk,
p.sectorFk, p.sectorFk,
st.description = 'previousPrepared' isPreviousPrepared, st.code = 'previousPrepared' isPreviousPrepared,
sc.itemPackingTypeFk sc.itemPackingTypeFk
FROM itemShelving ish FROM itemShelving ish
JOIN shelving sh ON sh.code = ish.shelvingFk JOIN shelving sh ON sh.code = ish.shelvingFk

View File

@ -85,7 +85,7 @@ BEGIN
JOIN client c ON c.id = t.clientFk JOIN client c ON c.id = t.clientFk
JOIN tmp.productionBuffer pb ON pb.ticketFk = t.id JOIN tmp.productionBuffer pb ON pb.ticketFk = t.id
JOIN packagingConfig pc JOIN packagingConfig pc
WHERE IF(st.description = 'previousByPacking', WHERE IF(st.code = 'previousByPacking',
i.`size` > pc.previousPreparationMinimumSize i.`size` > pc.previousPreparationMinimumSize
AND (MOD(TRUNCATE(isa.quantity,0), isa.packing)= 0 ), AND (MOD(TRUNCATE(isa.quantity,0), isa.packing)= 0 ),
TRUE) TRUE)

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

@ -4,22 +4,22 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`expedition_beforeInse
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
DECLARE intcounter INT; DECLARE intcounter INT;
DECLARE vShipFk INT; DECLARE vShipFk INT;
SET NEW.editorFk = account.myUser_getId(); SET NEW.editorFk = account.myUser_getId();
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
INNER JOIN ticket t1 ON e.ticketFk = t1.id INNER JOIN ticket t1 ON e.ticketFk = t1.id
LEFT JOIN ticketState ts ON ts.ticketFk = t1.id LEFT JOIN ticketState ts ON ts.ticketFk = t1.id
INNER JOIN ticket t2 ON t2.addressFk = t1.addressFk AND DATE(t2.shipped) = DATE(t1.shipped) INNER JOIN ticket t2 ON t2.addressFk = t1.addressFk AND DATE(t2.shipped) = DATE(t1.shipped)
AND t1.warehouseFk = t2.warehouseFk AND t1.warehouseFk = t2.warehouseFk
WHERE t2.id = NEW.ticketFk AND ts.alertLevel < 3 AND t1.companyFk = t2.companyFk WHERE t2.id = NEW.ticketFk AND ts.alertLevel < 3 AND t1.companyFk = t2.companyFk
AND t1.agencyModeFk = t2.agencyModeFk; AND t1.agencyModeFk = t2.agencyModeFk;
SET NEW.`counter` = intcounter; SET NEW.`counter` = intcounter;
END IF; END IF;

View File

@ -15,7 +15,7 @@ AS SELECT `ish`.`itemFk` AS `itemFk`,
`sh`.`parkingFk` AS `parkingFk`, `sh`.`parkingFk` AS `parkingFk`,
`ish`.`id` AS `itemShelvingFk`, `ish`.`id` AS `itemShelvingFk`,
`ish`.`created` AS `created`, `ish`.`created` AS `created`,
`st`.`description` = 'previousPrepared' AS `isPreviousPrepared` `st`.`code` = 'previousPrepared' AS `isPreviousPrepared`
FROM ( FROM (
( (
( (

View File

@ -1,49 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn2008`.`cc_to_iban`(cc VARCHAR(20))
RETURNS varchar(4) CHARSET utf8mb3 COLLATE utf8mb3_general_ci
DETERMINISTIC
BEGIN
DECLARE iban VARCHAR(4);
select
CONCAT('ES',
RIGHT(
concat(0,
98-
mod(
concat(
mod(
concat(
mod(
concat(
mod(
substring(cc,1,8),
97),
substring(cc,9,8)
),
97),
substring(
concat(
cc,
142800
),
17,
8
)
),
97),
substring(
concat(
cc,
142800
),
25,
2
)
),
97)
)
,2)
)into iban;
RETURN iban;
END$$
DELIMITER ;

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

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

@ -0,0 +1,60 @@
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,38 @@
ALTER TABLE vn.sectorType CHANGE description code varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL;
-- Si no pongo lo de bajo da error en la view vn.itemShelvingAvailable
CREATE OR REPLACE DEFINER=`root`@`localhost`
SQL SECURITY DEFINER
VIEW `vn`.`itemShelvingStock`
AS SELECT `ish`.`itemFk` AS `itemFk`,
sum(`ish`.`visible`) AS `visible`,
min(`ish`.`packing`) AS `packing`,
min(`ish`.`grouping`) AS `grouping`,
`s`.`description` AS `sector`,
sum(`ish`.`visible`) AS `visibleOriginal`,
0 AS `removed`,
`p`.`sectorFk` AS `sectorFk`,
`s`.`warehouseFk` AS `warehouseFk`,
`ish`.`shelvingFk` AS `shelvingFk`,
`p`.`code` AS `parkingCode`,
`sh`.`parkingFk` AS `parkingFk`,
`ish`.`id` AS `itemShelvingFk`,
`ish`.`created` AS `created`,
`st`.`code` = 'previousPrepared' AS `isPreviousPrepared`
FROM (
(
(
(
`vn`.`itemShelving` `ish`
LEFT JOIN `vn`.`shelving` `sh` ON(`sh`.`code` = `ish`.`shelvingFk`)
)
LEFT JOIN `vn`.`parking` `p` ON(`p`.`id` = `sh`.`parkingFk`)
)
LEFT JOIN `vn`.`sector` `s` ON(`s`.`id` = `p`.`sectorFk`)
)
LEFT JOIN `vn`.`sectorType` `st` ON(`st`.`id` = `s`.`typeFk`)
)
WHERE `ish`.`visible` <> 0
AND `p`.`sectorFk` <> 0
GROUP BY `ish`.`itemFk`,
`p`.`sectorFk`;

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

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

@ -1,344 +1,347 @@
{ {
"Phone format is invalid": "El formato del teléfono no es correcto", "Phone format is invalid": "El formato del teléfono no es correcto",
"You are not allowed to change the credit": "No tienes privilegios para modificar el crédito", "You are not allowed to change the credit": "No tienes privilegios para modificar el crédito",
"Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia", "Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia",
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado", "The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado", "Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
"Can't be blank": "No puede estar en blanco", "Can't be blank": "No puede estar en blanco",
"Invalid TIN": "NIF/CIF inválido", "Invalid TIN": "NIF/CIF inválido",
"TIN must be unique": "El NIF/CIF debe ser único", "TIN must be unique": "El NIF/CIF debe ser único",
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web", "A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
"Is invalid": "Es inválido", "Is invalid": "Es inválido",
"Quantity cannot be zero": "La cantidad no puede ser cero", "Quantity cannot be zero": "La cantidad no puede ser cero",
"Enter an integer different to zero": "Introduce un entero distinto de cero", "Enter an integer different to zero": "Introduce un entero distinto de cero",
"Package cannot be blank": "El embalaje no puede estar en blanco", "Package cannot be blank": "El embalaje no puede estar en blanco",
"The company name must be unique": "La razón social debe ser única", "The company name must be unique": "La razón social debe ser única",
"Invalid email": "Correo electrónico inválido", "Invalid email": "Correo electrónico inválido",
"The IBAN does not have the correct format": "El IBAN no tiene el formato correcto", "The IBAN does not have the correct format": "El IBAN no tiene el formato correcto",
"That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN", "That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN",
"That payment method requires a BIC": "El método de pago seleccionado requiere un BIC", "That payment method requires a BIC": "El método de pago seleccionado requiere un BIC",
"State cannot be blank": "El estado no puede estar en blanco", "State cannot be blank": "El estado no puede estar en blanco",
"Worker cannot be blank": "El trabajador no puede estar en blanco", "Worker cannot be blank": "El trabajador no puede estar en blanco",
"Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado", "Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado",
"can't be blank": "El campo no puede estar vacío", "can't be blank": "El campo no puede estar vacío",
"Observation type must be unique": "El tipo de observación no puede repetirse", "Observation type must be unique": "El tipo de observación no puede repetirse",
"The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero", "The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero",
"The grade must be similar to the last one": "El grade debe ser similar al último", "The grade must be similar to the last one": "El grade debe ser similar al último",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente", "Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco", "Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco", "Phone cannot be blank": "El teléfono no puede estar en blanco",
"Period cannot be blank": "El periodo no puede estar en blanco", "Period cannot be blank": "El periodo no puede estar en blanco",
"Choose a company": "Selecciona una empresa", "Choose a company": "Selecciona una empresa",
"Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto", "Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto",
"Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres", "Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres",
"Cannot be blank": "El campo no puede estar en blanco", "Cannot be blank": "El campo no puede estar en blanco",
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero", "The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco", "Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
"Description cannot be blank": "Se debe rellenar el campo de texto", "Description cannot be blank": "Se debe rellenar el campo de texto",
"The price of the item changed": "El precio del artículo cambió", "The price of the item changed": "El precio del artículo cambió",
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%", "The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
"The value should be a number": "El valor debe ser un numero", "The value should be a number": "El valor debe ser un numero",
"This order is not editable": "Esta orden no se puede modificar", "This order is not editable": "Esta orden no se puede modificar",
"You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado", "You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado",
"You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda", "You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda",
"is not a valid date": "No es una fecha valida", "is not a valid date": "No es una fecha valida",
"Barcode must be unique": "El código de barras debe ser único", "Barcode must be unique": "El código de barras debe ser único",
"The warehouse can't be repeated": "El almacén no puede repetirse", "The warehouse can't be repeated": "El almacén no puede repetirse",
"The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item", "The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item",
"The observation type can't be repeated": "El tipo de observación no puede repetirse", "The observation type can't be repeated": "El tipo de observación no puede repetirse",
"A claim with that sale already exists": "Ya existe una reclamación para esta línea", "A claim with that sale already exists": "Ya existe una reclamación para esta línea",
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo", "You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco", "Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco", "Agency cannot be blank": "La agencia no puede quedar en blanco",
"Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados", "Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe", "This address doesn't exist": "Este consignatario no existe",
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d", "You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
"You don't have enough privileges": "No tienes suficientes permisos", "You don't have enough privileges": "No tienes suficientes permisos",
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF", "Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos", "You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos",
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ", "INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ",
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado", "You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
"You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo", "You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo",
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco", "Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
"ORDER_EMPTY": "Cesta vacía", "ORDER_EMPTY": "Cesta vacía",
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto", "You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT", "NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido", "Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
"Street cannot be empty": "Dirección no puede estar en blanco", "Street cannot be empty": "Dirección no puede estar en blanco",
"City cannot be empty": "Ciudad no puede estar en blanco", "City cannot be empty": "Ciudad no puede estar en blanco",
"Code cannot be blank": "Código no puede estar en blanco", "Code cannot be blank": "Código no puede estar en blanco",
"You cannot remove this department": "No puedes eliminar este departamento", "You cannot remove this department": "No puedes eliminar este departamento",
"The extension must be unique": "La extensión debe ser unica", "The extension must be unique": "La extensión debe ser unica",
"The secret can't be blank": "La contraseña no puede estar en blanco", "The secret can't be blank": "La contraseña no puede estar en blanco",
"We weren't able to send this SMS": "No hemos podido enviar el SMS", "We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado", "This client can't be invoiced": "Este cliente no puede ser facturado",
"You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa", "You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
"This ticket can't be invoiced": "Este ticket no puede ser facturado", "This ticket can't be invoiced": "Este ticket no puede ser facturado",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado", "You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado", "This ticket can not be modified": "Este ticket no puede ser modificado",
"The introduced hour already exists": "Esta hora ya ha sido introducida", "The introduced hour already exists": "Esta hora ya ha sido introducida",
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes", "INFINITE_LOOP": "Existe una dependencia entre dos Jefes",
"The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas", "The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas",
"NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros", "NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros",
"ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado", "ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado",
"The current ticket can't be modified": "El ticket actual no puede ser modificado", "The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada", "The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas", "The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)", "The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)",
"Please select at least one sale": "Por favor selecciona al menos una linea", "Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket", "All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada", "NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"This item doesn't exists": "El artículo no existe", "This item doesn't exists": "El artículo no existe",
"NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada", "NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"Extension format is invalid": "El formato de la extensión es inválido", "Extension format is invalid": "El formato de la extensión es inválido",
"Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket", "Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket",
"This item is not available": "Este artículo no está disponible", "This item is not available": "Este artículo no está disponible",
"This postcode already exists": "Este código postal ya existe", "This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "El concepto no puede quedar en blanco", "Concept cannot be blank": "El concepto no puede quedar en blanco",
"File doesn't exists": "El archivo no existe", "File doesn't exists": "El archivo no existe",
"You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias", "You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias",
"This ticket is already on weekly tickets": "Este ticket ya está en tickets programados", "This ticket is already on weekly tickets": "Este ticket ya está en tickets programados",
"Ticket id cannot be blank": "El id de ticket no puede quedar en blanco", "Ticket id cannot be blank": "El id de ticket no puede quedar en blanco",
"Weekday cannot be blank": "El día de la semana no puede quedar en blanco", "Weekday cannot be blank": "El día de la semana no puede quedar en blanco",
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado", "You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto", "The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
"Invalid quantity": "Cantidad invalida", "Invalid quantity": "Cantidad invalida",
"This postal code is not valid": "Este código postal no es válido", "This postal code is not valid": "Este código postal no es válido",
"is invalid": "es inválido", "is invalid": "es inválido",
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto", "The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
"The department name can't be repeated": "El nombre del departamento no puede repetirse", "The department name can't be repeated": "El nombre del departamento no puede repetirse",
"This phone already exists": "Este teléfono ya existe", "This phone already exists": "Este teléfono ya existe",
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos", "You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado", "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada", "You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero", "You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
"You should specify a date": "Debes especificar una fecha", "You should specify a date": "Debes especificar una fecha",
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin", "You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin",
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin", "Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin",
"You should mark at least one week day": "Debes marcar al menos un día de la semana", "You should mark at least one week day": "Debes marcar al menos un día de la semana",
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío", "Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios", "Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios", "Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
"Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", "Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}",
"Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", "Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})", "Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})", "Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})",
"State": "Estado", "State": "Estado",
"regular": "normal", "regular": "normal",
"reserved": "reservado", "reserved": "reservado",
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", "Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
"Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}", "Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}",
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Change quantity": "{{concept}} cambia de {{oldQuantity}} a {{newQuantity}}", "Change quantity": "{{concept}} cambia de {{oldQuantity}} a {{newQuantity}}",
"Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*", "Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*",
"Claim state has changed to": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *{{newState}}*", "Claim state has changed to": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *{{newState}}*",
"Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}", "Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}",
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto", "ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",
"Distance must be lesser than 1000": "La distancia debe ser inferior a 1000", "Distance must be lesser than 1000": "La distancia debe ser inferior a 1000",
"This ticket is deleted": "Este ticket está eliminado", "This ticket is deleted": "Este ticket está eliminado",
"Unable to clone this travel": "No ha sido posible clonar este travel", "Unable to clone this travel": "No ha sido posible clonar este travel",
"This thermograph id already exists": "La id del termógrafo ya existe", "This thermograph id already exists": "La id del termógrafo ya existe",
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante", "Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
"ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA", "ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA",
"Invalid password": "Invalid password", "Invalid password": "Invalid password",
"Password does not meet requirements": "La contraseña no cumple los requisitos", "Password does not meet requirements": "La contraseña no cumple los requisitos",
"Role already assigned": "Rol ya asignado", "Role already assigned": "Rol ya asignado",
"Invalid role name": "Nombre de rol no válido", "Invalid role name": "Nombre de rol no válido",
"Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase", "Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase",
"Email already exists": "El correo ya existe", "Email already exists": "El correo ya existe",
"User already exists": "El/La usuario/a ya existe", "User already exists": "El/La usuario/a ya existe",
"Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral", "Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral",
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ", "Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.", "Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.", "Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})", "I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})", "I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación", "You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"agencyModeFk": "Agencia", "agencyModeFk": "Agencia",
"clientFk": "Cliente", "clientFk": "Cliente",
"zoneFk": "Zona", "zoneFk": "Zona",
"warehouseFk": "Almacén", "warehouseFk": "Almacén",
"shipped": "F. envío", "shipped": "F. envío",
"landed": "F. entrega", "landed": "F. entrega",
"addressFk": "Consignatario", "addressFk": "Consignatario",
"companyFk": "Empresa", "companyFk": "Empresa",
"The social name cannot be empty": "La razón social no puede quedar en blanco", "The social name cannot be empty": "La razón social no puede quedar en blanco",
"The nif cannot be empty": "El NIF no puede quedar en blanco", "The nif cannot be empty": "El NIF no puede quedar en blanco",
"You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados", "You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados",
"ASSIGN_ZONE_FIRST": "Asigna una zona primero", "ASSIGN_ZONE_FIRST": "Asigna una zona primero",
"Amount cannot be zero": "El importe no puede ser cero", "Amount cannot be zero": "El importe no puede ser cero",
"Company has to be official": "Empresa inválida", "Company has to be official": "Empresa inválida",
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria", "You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas", "Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta", "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*", "New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*", "New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío", "Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
"This BIC already exist.": "Este BIC ya existe.", "This BIC already exist.": "Este BIC ya existe.",
"That item doesn't exists": "Ese artículo no existe", "That item doesn't exists": "Ese artículo no existe",
"There's a new urgent ticket:": "Hay un nuevo ticket urgente:", "There's a new urgent ticket:": "Hay un nuevo ticket urgente:",
"Invalid account": "Cuenta inválida", "Invalid account": "Cuenta inválida",
"Compensation account is empty": "La cuenta para compensar está vacia", "Compensation account is empty": "La cuenta para compensar está vacia",
"This genus already exist": "Este genus ya existe", "This genus already exist": "Este genus ya existe",
"This specie already exist": "Esta especie ya existe", "This specie already exist": "Esta especie ya existe",
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "Ninguno", "None": "Ninguno",
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada", "The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada",
"Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'", "Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'",
"This document already exists on this ticket": "Este documento ya existe en el ticket", "This document already exists on this ticket": "Este documento ya existe en el ticket",
"Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables", "Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables",
"You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes", "You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes",
"nickname": "nickname", "nickname": "nickname",
"INACTIVE_PROVIDER": "Proveedor inactivo", "INACTIVE_PROVIDER": "Proveedor inactivo",
"This client is not invoiceable": "Este cliente no es facturable", "This client is not invoiceable": "Este cliente no es facturable",
"serial non editable": "Esta serie no permite asignar la referencia", "serial non editable": "Esta serie no permite asignar la referencia",
"Max shipped required": "La fecha límite es requerida", "Max shipped required": "La fecha límite es requerida",
"Can't invoice to future": "No se puede facturar a futuro", "Can't invoice to future": "No se puede facturar a futuro",
"Can't invoice to past": "No se puede facturar a pasado", "Can't invoice to past": "No se puede facturar a pasado",
"This ticket is already invoiced": "Este ticket ya está facturado", "This ticket is already invoiced": "Este ticket ya está facturado",
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero", "A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa", "A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes", "Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes", "Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio", "Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito", "You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas", "You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas",
"Amounts do not match": "Las cantidades no coinciden", "Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exist": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'", "The PDF document does not exist": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos", "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días", "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día", "The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día", "The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado", "You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
"The account size must be exactly 10 characters": "El tamaño de la cuenta debe ser exactamente de 10 caracteres", "The account size must be exactly 10 characters": "El tamaño de la cuenta debe ser exactamente de 10 caracteres",
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas", "Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
"You don't have privileges to create refund": "No tienes permisos para crear un abono", "You don't have privileges to create refund": "No tienes permisos para crear un abono",
"The item is required": "El artículo es requerido", "The item is required": "El artículo es requerido",
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo", "The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro", "date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada", "reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono", "This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "Sin negativos", "isWithoutNegatives": "Sin negativos",
"routeFk": "routeFk", "routeFk": "routeFk",
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador", "Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
"No hay un contrato en vigor": "No hay un contrato en vigor", "No hay un contrato en vigor": "No hay un contrato en vigor",
"No se permite fichar a futuro": "No se permite fichar a futuro", "No se permite fichar a futuro": "No se permite fichar a futuro",
"No está permitido trabajar": "No está permitido trabajar", "No está permitido trabajar": "No está permitido trabajar",
"Fichadas impares": "Fichadas impares", "Fichadas impares": "Fichadas impares",
"Descanso diario 12h.": "Descanso diario 12h.", "Descanso diario 12h.": "Descanso diario 12h.",
"Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.", "Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.",
"Dirección incorrecta": "Dirección incorrecta", "Dirección incorrecta": "Dirección incorrecta",
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador", "Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador", "Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente", "Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe", "This route does not exists": "Esta ruta no existe",
"Claim pickup order sent": "Reclamación Orden de recogida enviada [{{claimId}}]({{{claimUrl}}}) al cliente *{{clientName}}*", "Claim pickup order sent": "Reclamación Orden de recogida enviada [{{claimId}}]({{{claimUrl}}}) al cliente *{{clientName}}*",
"You don't have grant privilege": "No tienes privilegios para dar privilegios", "You don't have grant privilege": "No tienes privilegios para dar privilegios",
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario", "You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario",
"Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) fusionado con [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})", "Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) fusionado con [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})",
"Already has this status": "Ya tiene este estado", "Already has this status": "Ya tiene este estado",
"There aren't records for this week": "No existen registros para esta semana", "There aren't records for this week": "No existen registros para esta semana",
"Empty data source": "Origen de datos vacio", "Empty data source": "Origen de datos vacio",
"App locked": "Aplicación bloqueada por el usuario {{userId}}", "App locked": "Aplicación bloqueada por el usuario {{userId}}",
"Email verify": "Correo de verificación", "Email verify": "Correo de verificación",
"Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment", "Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment",
"Receipt's bank was not found": "No se encontró el banco del recibo", "Receipt's bank was not found": "No se encontró el banco del recibo",
"This receipt was not compensated": "Este recibo no ha sido compensado", "This receipt was not compensated": "Este recibo no ha sido compensado",
"Client's email was not found": "No se encontró el email del cliente", "Client's email was not found": "No se encontró el email del cliente",
"Negative basis": "Base negativa", "Negative basis": "Base negativa",
"This worker code already exists": "Este codigo de trabajador ya existe", "This worker code already exists": "Este codigo de trabajador ya existe",
"This personal mail already exists": "Este correo personal ya existe", "This personal mail already exists": "Este correo personal ya existe",
"This worker already exists": "Este trabajador ya existe", "This worker already exists": "Este trabajador ya existe",
"App name does not exist": "El nombre de aplicación no es válido", "App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo", "Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9", "Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload delivery note": "Error al subir albarán {{id}}", "Failed to upload delivery note": "Error al subir albarán {{id}}",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe", "The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar", "It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo", "It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas", "It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.", "A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
"There is no assigned email for this client": "No hay correo asignado para este cliente", "There is no assigned email for this client": "No hay correo asignado para este cliente",
"Exists an invoice with a future date": "Existe una factura con fecha posterior", "Exists an invoice with a future date": "Existe una factura con fecha posterior",
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite", "Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
"Warehouse inventory not set": "El almacén inventario no está establecido", "Warehouse inventory not set": "El almacén inventario no está establecido",
"This locker has already been assigned": "Esta taquilla ya ha sido asignada", "This locker has already been assigned": "Esta taquilla ya ha sido asignada",
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %d", "Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %d",
"Not exist this branch": "La rama no existe", "Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado", "This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe", "Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo", "Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"Insert a date range": "Inserte un rango de fechas", "Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}}", "Added observation": "{{user}} añadió esta observacion: {{text}}",
"Comment added to client": "Observación añadida al cliente {{clientFk}}", "Comment added to client": "Observación añadida al cliente {{clientFk}}",
"Invalid auth code": "Código de verificación incorrecto", "Invalid auth code": "Código de verificación incorrecto",
"Invalid or expired verification code": "Código de verificación incorrecto o expirado", "Invalid or expired verification code": "Código de verificación incorrecto o expirado",
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen", "Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
"company": "Compañía", "company": "Compañía",
"country": "País", "country": "País",
"clientId": "Id cliente", "clientId": "Id cliente",
"clientSocialName": "Cliente", "clientSocialName": "Cliente",
"amount": "Importe", "amount": "Importe",
"taxableBase": "Base", "taxableBase": "Base",
"ticketFk": "Id ticket", "ticketFk": "Id ticket",
"isActive": "Activo", "isActive": "Activo",
"hasToInvoice": "Facturar", "hasToInvoice": "Facturar",
"isTaxDataChecked": "Datos comprobados", "isTaxDataChecked": "Datos comprobados",
"comercialId": "Id comercial", "comercialId": "Id comercial",
"comercialName": "Comercial", "comercialName": "Comercial",
"Pass expired": "La contraseña ha caducado, cambiela desde Salix", "Pass expired": "La contraseña ha caducado, cambiela desde Salix",
"Invalid NIF for VIES": "Invalid NIF for VIES", "Invalid NIF for VIES": "Invalid NIF for VIES",
"Ticket does not exist": "Este ticket no existe", "Ticket does not exist": "Este ticket no existe",
"Ticket is already signed": "Este ticket ya ha sido firmado", "Ticket is already signed": "Este ticket ya ha sido firmado",
"Authentication failed": "Autenticación fallida", "Authentication failed": "Autenticación fallida",
"You can't use the same password": "No puedes usar la misma contraseña", "You can't use the same password": "No puedes usar la misma contraseña",
"You can only add negative amounts in refund tickets": "Solo se puede añadir cantidades negativas en tickets abono", "You can only add negative amounts in refund tickets": "Solo se puede añadir cantidades negativas en tickets abono",
"Fecha fuera de rango": "Fecha fuera de rango", "Fecha fuera de rango": "Fecha fuera de rango",
"Error while generating PDF": "Error al generar PDF", "Error while generating PDF": "Error al generar PDF",
"Error when sending mail to client": "Error al enviar el correo al cliente", "Error when sending mail to client": "Error al enviar el correo al cliente",
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico", "Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado", "The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d", "Valid priorities": "Prioridades válidas: %d",
"hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}", "hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
"hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}", "hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado", "You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s", "This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias", "The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado", "You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado", "This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.", "You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked": "Este ticket está bloqueado.", "This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.", "This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.", "The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula", "Social name should be uppercase": "La razón social debe ir en mayúscula",
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula", "Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
"Ticket without Route": "Ticket sin ruta", "Ticket without Route": "Ticket sin ruta",
"Select a different client": "Seleccione un cliente distinto", "Select a different client": "Seleccione un cliente distinto",
"Fill all the fields": "Rellene todos los campos", "Fill all the fields": "Rellene todos los campos",
"The response is not a PDF": "La respuesta no es un PDF", "The response is not a PDF": "La respuesta no es un PDF",
"Booking completed": "Reserva completada", "Booking completed": "Reserva completada",
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
"Incoterms data for consignee is missing": "Faltan los datos de los Incoterms para el consignatario", "Incoterms data for consignee is missing": "Faltan los datos de los Incoterms para el consignatario",
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada", "The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
"User disabled": "Usuario desactivado", "User disabled": "Usuario desactivado",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima", "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas", "Cannot past travels with entries": "No se pueden pasar envíos con entradas",
"It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}", "It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
"This claim has been updated": "La reclamación con Id: {{claimId}}, ha sido actualizada", "This claim has been updated": "La reclamación con Id: {{claimId}}, ha sido actualizada",
"This user does not have an assigned tablet": "Este usuario no tiene tablet asignada", "This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
"Incorrect pin": "Pin incorrecto", "Incorrect pin": "Pin incorrecto",
"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 warehouse has not dms": "El Almacén no acepta documentos", "this warehouse has not dms": "El Almacén no acepta documentos",
"Name should be uppercase": "El nombre debe ir en mayúscula", "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",
"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"
}

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) { for (const id of ids) {
if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large'); ctx.args = ctx.args || {};
const response = await axios.get( ctx.args.id = Number(id);
`${ctx.req.headers.referer}api/Routes/${id}/cmr?access_token=${token.id}`, { const [data] = await models.Route.cmr(ctx, myOptions);
...myOptions, zip.file(`${id}.pdf`, data, {binary: true});
responseType: 'arraybuffer',
});
if (response.headers['content-type'] !== 'application/pdf')
throw new UserError(`The response is not a PDF`);
zip.file(`${id}.pdf`, response.data, { binary: true });
}
const zipStream = zip.generateNodeStream({ streamFiles: true });
return [zipStream, 'application/zip', `filename="cmrs.zip"`];
} catch (e) {
throw e;
} }
const zipStream = zip.generateNodeStream({streamFiles: true});
return [zipStream, 'application/zip', `filename="cmrs.zip"`];
}; };
}; };

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

@ -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',
nif: '06089160W',
account: '4100000001',
sageTaxTypeFk: 4,
sageWithholdingFk: 1,
sageTransactionTypeFk: 1,
postCode: '15214',
city: 'PONTEVEDRA',
provinceFk: 1,
countryFk: 1,
};
it('should return an error if the user is not administrative', async() => { const name = 'NEW PLANTS';
const ctx = {req: {accessToken: {userId: employeeId}}}; const city = 'PONTEVEDRA';
ctx.args = {}; const nif = 'A68446004';
const account = '4000000005';
const sageTaxTypeFk = 5;
const sageWithholdingFk = 2;
const sageTransactionTypeFk = 2;
const postCode = '46460';
const phone = 456129367;
const street = ' Fake address 12 3 flat';
const provinceFk = 2;
const countryFk = 1;
const supplierActivityFk = 'animals';
const healthRegister = '400664487H';
let error; let ctx;
await app.models.Supplier.updateFiscalData(ctx, supplierId) let options;
.catch(e => { let tx;
error = e;
});
expect(error.message).toBeDefined(); beforeEach(async() => {
}); ctx = {
req: {
it('should check that the supplier fiscal data is untainted', async() => { accessToken: {userId: buyerId},
const supplier = await app.models.Supplier.findById(supplierId); headers: {origin: 'http://localhost'},
__: value => value
expect(supplier.name).toEqual(defaultData.name); },
expect(supplier.nif).toEqual(defaultData.nif); args: {}
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,9 @@ 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 ticket;
let dms; let dms;
let gestDocCreated = false; let gestDocCreated;
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
@ -44,6 +45,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,98 +59,105 @@ 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'] include: [
{
relation: 'dms',
fields: ['id'],
scope: {
where: {dmsTypeFk: dmsTypeTicket.id}
}
}
]
}, myOptions); }, myOptions);
if (ticketDms?.dms()?.id) return true;
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) { async function createGestDoc() {
const ticket = await models.Ticket.findById(id,
{
include: [
{
relation: 'warehouse',
scope: {
fields: ['id']
}
}, {
relation: 'client',
scope: {
fields: ['name']
}
}, {
relation: 'route',
scope: {
fields: ['id']
}
}
]
}, myOptions);
const dmsType = await models.DmsType.findOne({where: {code: 'Ticket'}, fields: ['id']}, myOptions);
const ctxUploadFile = Object.assign({}, ctx); const ctxUploadFile = Object.assign({}, ctx);
if (ticket.route() === null)
throw new UserError('Ticket without route');
ctxUploadFile.args = { ctxUploadFile.args = {
warehouseId: ticket.warehouseFk, warehouseId: ticket.warehouseFk,
companyId: ticket.companyFk, companyId: ticket.companyFk,
dmsTypeId: dmsType.id, dmsTypeId: dmsTypeTicket.id,
reference: '', reference: ticket.id,
description: `Firma del cliente - Ruta ${ticket.route().id}`, description: `Firma del cliente - Ruta ${ticket.route().id}`,
hasFile: false contentType: 'image/png',
hasFile: true
}; };
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions); dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
gestDocCreated = true; gestDocCreated = true;
} }
try { try {
let externalTickets = [];
for (const ticketId of tickets) { for (const ticketId of tickets) {
const ticketState = await models.TicketState.findOne( ticket = await models.Ticket.findById(ticketId, {
{where: {ticketFk: ticketId}, include: [{
fields: ['alertLevel'] relation: 'address',
}, myOptions); scope: {
include: {
relation: 'province',
scope: {
include: {
relation: 'country',
scope: {
fields: ['code']
}
}
}
}
}
}, {
relation: 'route',
scope: {
fields: ['id']
}
}]
}, myOptions);
const packedAlertLevel = await models.AlertLevel.findOne({where: {code: 'PACKED'}, const ticketState = await models.TicketState.findOne({
where: {ticketFk: ticketId},
fields: ['alertLevel']
}, myOptions);
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) && !gestDocCreated)
await createGestDoc(ticketId);
await models.TicketDms.create({ticketFk: ticketId, dmsFk: dms[0].id}, myOptions); await models.TicketDms.create({ticketFk: ticketId, dmsFk: dms[0].id}, myOptions);
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();
await models.Route.cmrEmail(ctx, externalTickets);
return; return;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

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 = ?