7167-testToMaster_2414 #2244

Merged
alexm merged 643 commits from 7167-testToMaster_2414 into master 2024-04-04 05:32:41 +00:00
41 changed files with 569 additions and 140 deletions
Showing only changes of commit 1658838c87 - Show all commits

1
Jenkinsfile vendored
View File

@ -71,6 +71,7 @@ pipeline {
stage('Back') {
steps {
sh 'pnpm install --prefer-offline'
sh 'pnpx puppeteer browsers install chrome'
}
}
stage('Print') {

View File

@ -387,7 +387,7 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
(1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','loses');
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), UPPER(CONCAT(name, 'Social')), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
FROM `account`.`role` `r`
WHERE `r`.`hasLogin` = 1;
@ -564,13 +564,13 @@ INSERT INTO `vn`.`supplierActivity`(`code`, `name`)
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`, `commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`, `workerFk`, `supplierActivityFk`, `isPayMethodChecked`, `healthRegister`)
VALUES
(1, 'PLANTS SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
(2, 'FARMER KING', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
(1, 'PLANTS SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'GOTHAM', 1, 46000, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
(2, 'FARMER KING', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 46000, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
(69, 'PACKAGING', 'Packaging nick', 4100000069, 1, '94935005K', 0, util.VN_CURDATE(), 1, 'supplier address 5', 'ASGARD', 3, 46600, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
(442, 'VERDNATURA LEVANTE SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
(442, 'VERDNATURA LEVANTE SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 46000, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
(567, 'HOLLAND', 'Holland nick', 4000020567, 1, '14364089Z', 0, util.VN_CURDATE(), 1, 'supplier address 6', 'ASGARD', 3, 46600, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
(791, 'BROS SL', 'Bros nick', 5115000791, 1, '37718083S', 0, util.VN_CURDATE(), 1, 'supplier address 7', 'ASGARD', 3, 46600, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
(1381, 'ORNAMENTALES', 'Ornamentales', 7185001381, 1, '07972486L', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
(1381, 'ORNAMENTALES', 'Ornamentales', 7185001381, 1, '07972486L', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 46000, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
INSERT INTO `vn`.`supplierAddress`(`id`, `supplierFk`, `nickname`, `street`, `provinceFk`, `postalCode`, `city`, `phone`, `mobile`)
VALUES
@ -2409,7 +2409,8 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`)
(17, 'cmr', 1, 1, 'cmr'),
(18, 'dua', NULL, NULL, 'dua'),
(19, 'inmovilizado', NULL, NULL, 'fixedAssets'),
(20, 'Reclamación', 1, 1, 'claim');
(20, 'Reclamación', 1, 1, 'claim'),
(21, 'Entrada', 1, 1, 'entry');
INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`)
VALUES
@ -2420,7 +2421,8 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', util.VN_CURDATE()),
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', util.VN_CURDATE()),
(7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()),
(8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE());
(8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()),
(9, 21, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'ENTRADA ID 1', util.VN_CURDATE());
INSERT INTO `vn`.`claimDms`(`claimFk`, `dmsFk`)
VALUES
@ -3064,6 +3066,10 @@ INSERT INTO `vn`.`clientSms` (`id`, `clientFk`, `smsFk`, `ticketFk`)
(13, 1101, 1, NULL),
(14, 1101, 4, 27);
INSERT INTO `vn`.`entryDms`(`entryFk`, `dmsFk`, `editorFk`)
VALUES
(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'),

View File

@ -38,7 +38,8 @@ BEGIN
DELETE FROM saleTracking WHERE created < vOneYearAgo;
DELETE FROM ticketTracking WHERE created < v18Month;
DELETE tobs FROM ticketObservation tobs
JOIN ticket t ON tobs.ticketFk = t.id WHERE t.shipped < TIMESTAMPADD(YEAR,-2,util.VN_CURDATE());
JOIN ticket t ON tobs.ticketFk = t.id
WHERE t.shipped < v5Years;
DELETE sc.* FROM saleCloned sc JOIN sale s ON s.id = sc.saleClonedFk JOIN ticket t ON t.id = s.ticketFk WHERE t.shipped < vOneYearAgo;
DELETE FROM sharingCart where ended < vDateShort;
DELETE FROM sharingClient where ended < vDateShort;

View File

@ -75,7 +75,7 @@ BEGIN
SET vDated = DATE(vTimed);
SELECT IF(pc.code = 'driveCE',
SELECT IF(pc.code = 'driverCE',
wc.dayBreakDriver,
wc.dayBreak),
wc.shortWeekBreak,

View File

@ -23,6 +23,12 @@ BEGIN
IF vOldBusinessFk IS NULL THEN
CALL account.account_enable(vSelf);
UPDATE client c
JOIN payMethod pm ON pm.code = 'bankDraft'
SET c.payMethodFk = pm.id
WHERE c.id = vSelf
AND c.iban;
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,12 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`entryDms_afterDelete`
AFTER DELETE ON `entryDms`
FOR EACH ROW
BEGIN
INSERT INTO entryLog
SET `action` = 'delete',
`changedModel` = 'EntryDms',
`changedModelId` = OLD.entryFk,
`userFk` = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`entryDms_beforeInsert`
BEFORE INSERT ON `entryDms`
FOR EACH ROW
BEGIN
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`entryDms_beforeUpdate`
BEFORE UPDATE ON `entryDms`
FOR EACH ROW
BEGIN
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -0,0 +1,12 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`supplierDms_afterDelete`
AFTER DELETE ON `supplierDms`
FOR EACH ROW
BEGIN
INSERT INTO clientLog
SET `action` = 'delete',
`changedModel` = 'supplierDms',
`changedModelId` = OLD.dmsFk,
`userFk` = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`supplierDms_beforeInsert`
BEFORE INSERT ON `supplierDms`
FOR EACH ROW
BEGIN
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`supplierDms_beforeUpdate`
BEFORE UPDATE ON `supplierDms`
FOR EACH ROW
BEGIN
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -3,6 +3,8 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`travel_beforeUpdate`
BEFORE UPDATE ON `travel`
FOR EACH ROW
BEGIN
DECLARE vHasAnyInvoiceBooked BOOL;
SET NEW.editorFk = account.myUser_getId();
IF NOT (NEW.landed <=> OLD.landed)
@ -17,5 +19,18 @@ BEGIN
IF NOT (NEW.warehouseInFk <=> OLD.warehouseInFk) THEN
CALL travel_checkWarehouseIsFeedStock(NEW.warehouseInFk);
END IF;
IF NOT (NEW.awbFk <=> OLD.awbFk)THEN
SELECT COUNT(*) INTO vHasAnyInvoiceBooked
FROM travel t
JOIN entry e ON e.travelFk = t.id
JOIN invoiceIn ii ON ii.id = e.invoiceInFk
WHERE t.id = NEW.id
AND ii.isBooked;
IF vHasAnyInvoiceBooked THEN
CALL util.throw('The travel has entries with booked invoices');
END IF;
END IF;
END$$
DELIMITER ;

View File

@ -25,11 +25,6 @@ proc: BEGIN
DELETE FROM Movimientos_mark WHERE odbc_date < vDate;
DELETE FROM Splits WHERE Fecha < vDate18;
DELETE tobs
FROM ticket_observation tobs
JOIN Tickets t ON tobs.Id_Ticket = t.Id_Ticket
WHERE t.Fecha < vDate;
DELETE tobs
FROM movement_label tobs
JOIN Movimientos m ON tobs.Id_Movimiento = m.Id_Movimiento

View File

@ -0,0 +1,15 @@
CREATE OR REPLACE TABLE `vn`.`entryDms` (
`entryFk` int(11) NOT NULL,
`dmsFk` int(11) NOT NULL,
`editorFk` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`entryFk`,`dmsFk`),
KEY `gestdoc_id` (`dmsFk`),
KEY `entryDms_editor` (`editorFk`),
CONSTRAINT `entryDms_dms` FOREIGN KEY (`dmsFk`) REFERENCES `dms` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `entryDms_editor` FOREIGN KEY (`editorFk`) REFERENCES `account`.`user` (`id`),
CONSTRAINT `entryDms_entry` FOREIGN KEY (`entryFk`) REFERENCES `entry` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('EntryDms', '*', '*', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,2 @@
INSERT INTO `vn`.`dmsType` (code, name, path__, writeRoleFk, readRoleFk, monthToDelete)
VALUES('entry', 'Entrada', '', 1, 1, NULL);

View File

@ -0,0 +1,6 @@
ALTER TABLE vn.professionalCategory DROP COLUMN IF EXISTS code;
ALTER TABLE IF EXISTS vn.professionalCategory ADD COLUMN code VARCHAR(25) UNIQUE DEFAULT NULL;
UPDATE vn.professionalCategory
SET code = 'driverCE'
WHERE name = 'Conductor C + E';

View File

@ -0,0 +1,25 @@
DELETE FROM vn.supplierDms
WHERE dmsFk IN (
SELECT sd.dmsFk
FROM vn.supplierDms sd
LEFT JOIN vn.dms d ON d.id = sd.dmsFk
WHERE d.id IS NULL
);
DELETE FROM vn.supplierDms
WHERE supplierFk IN (
SELECT sd.supplierFk
FROM vn.supplierDms sd
LEFT JOIN vn.supplier s ON s.id = sd.supplierFk
WHERE s.id IS NULL
);
ALTER TABLE `vn`.`supplierDms`
MODIFY COLUMN supplierFk int(10) unsigned NOT NULL,
ADD editorFk INT UNSIGNED NULL,
ADD CONSTRAINT user_Fk FOREIGN KEY (editorFk) REFERENCES account.`user`(id),
ADD CONSTRAINT dms_Fk FOREIGN KEY (dmsFk) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT supplier_Fk FOREIGN KEY (supplierFk) REFERENCES vn.supplier(id) ON UPDATE CASCADE;
ALTER TABLE `vn`.`supplierLog`
MODIFY COLUMN `changedModel` ENUM('Supplier','SupplierAddress','SupplierAccount','SupplierContact','SupplierDms') NOT NULL DEFAULT 'Supplier';

View File

@ -0,0 +1,5 @@
DELETE FROM vn.entryObservation
WHERE observationTypeFk IS NULL;
ALTER TABLE vn.entryObservation
MODIFY COLUMN observationTypeFk tinyint(3) unsigned NOT NULL;

View File

@ -0,0 +1,12 @@
ALTER TABLE vn.country
MODIFY COLUMN code varchar(2) NOT NULL;
ALTER TABLE vn.country
ADD CONSTRAINT country_unique UNIQUE KEY (code);
ALTER TABLE vn.transitoryDuaUnified
ADD countryCodeFk varchar(2) DEFAULT 'EC' NOT NULL;
ALTER TABLE vn.transitoryDuaUnified
ADD CONSTRAINT transitoryDuaUnified_country_FK FOREIGN KEY (countryCodeFk)
REFERENCES vn.country(code);

View File

@ -333,9 +333,12 @@
"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 user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
"Incorrect pin": "Pin incorrecto",
"Field are invalid": "El campo '{{tag}}' no es válido",
"Incorrect pin": "Pin incorrecto.",
"You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar",
"this warehouse has not dms": "El Almacén no acepta documentos",
"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",
@ -343,7 +346,6 @@
"You cannot update these fields": "No puedes actualizar estos campos",
"CountryFK cannot be empty": "El país no puede estar vacío",
"Cmr file does not exist": "El archivo del cmr no existe",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
"No tickets to invoice": "No hay tickets para facturar",
"This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario"
"This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias"
}

View File

@ -103,6 +103,20 @@
"video/mp4"
]
},
"entryStorage": {
"name": "entryStorage",
"connector": "loopback-component-storage",
"provider": "filesystem",
"root": "./storage/dms",
"maxFileSize": "31457280",
"allowedContentTypes": [
"image/png",
"image/jpeg",
"image/jpg",
"image/webp",
"video/mp4"
]
},
"accessStorage": {
"name": "accessStorage",
"connector": "loopback-component-storage",

View File

@ -26,23 +26,23 @@ module.exports = Self => {
},
{
arg: 'street',
type: 'string'
type: 'any'
},
{
arg: 'postcode',
type: 'string'
type: 'any'
},
{
arg: 'city',
type: 'string'
type: 'any'
},
{
arg: 'countryFk',
type: 'number'
type: 'any'
},
{
arg: 'provinceFk',
type: 'number'
type: 'any'
},
{
arg: 'sageTaxTypeFk',
@ -94,7 +94,7 @@ module.exports = Self => {
},
{
arg: 'despiteOfClient',
type: 'number'
type: 'any'
},
{
arg: 'hasIncoterms',

View File

@ -0,0 +1,59 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('downloadFile', {
description: 'Get the entry file',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'Number',
description: 'The document id',
http: {source: 'path'}
}
],
returns: [
{
arg: 'body',
type: 'file',
root: true
},
{
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
},
{
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: `/:id/downloadFile`,
verb: 'GET'
}
});
Self.downloadFile = async function(ctx, id) {
const models = Self.app.models;
const EntryContainer = models.EntryContainer;
const dms = await models.Dms.findById(id);
const pathHash = EntryContainer.getHash(dms.id);
try {
await EntryContainer.getFile(pathHash, dms.file);
} catch (e) {
if (e.code != 'ENOENT')
throw e;
const error = new UserError(`File doesn't exists`);
error.statusCode = 404;
throw error;
}
const stream = EntryContainer.downloadStream(pathHash, dms.file);
return [stream, dms.contentType, `filename="${dms.file}"`];
};
};

View File

@ -0,0 +1,53 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('removeFile', {
description: 'Removes a entry document',
accessType: 'WRITE',
accepts: {
arg: 'id',
type: 'number',
description: 'The document id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/removeFile`,
verb: 'POST'
}
});
Self.removeFile = async(ctx, id, options) => {
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const targetEntryDms = await Self.findById(id, null, myOptions);
const targetDms = await Self.app.models.Dms.removeFile(ctx, targetEntryDms.dmsFk, myOptions);
if (!targetDms)
throw new UserError('Try again');
const entryDmsDestroyed = await targetEntryDms.destroy(myOptions);
if (tx) await tx.commit();
return entryDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,86 @@
module.exports = Self => {
Self.remoteMethodCtx('uploadFile', {
description: 'Upload and attach a file',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
description: 'The entry id',
http: {source: 'path'}
},
{
arg: 'warehouseId',
type: 'number',
description: 'The warehouse id',
required: true
},
{
arg: 'companyId',
type: 'number',
description: 'The company id',
required: true
},
{
arg: 'dmsTypeId',
type: 'number',
description: 'The dms type id',
required: true
},
{
arg: 'reference',
type: 'string',
required: true
},
{
arg: 'description',
type: 'string',
required: true
},
{
arg: 'hasFile',
type: 'boolean',
description: 'True if has an attached file',
required: true
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/uploadFile`,
verb: 'POST'
}
});
Self.uploadFile = async(ctx, id, options) => {
const {Dms, EntryDms} = 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 {
const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
const promises = uploadedFiles.map(dms => EntryDms.create({
entryFk: id,
dmsFk: dms.id
}, myOptions));
await Promise.all(promises);
if (tx) await tx.commit();
return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -2,6 +2,12 @@
"Entry": {
"dataSource": "vn"
},
"EntryDms": {
"dataSource": "vn"
},
"EntryContainer": {
"dataSource": "entryStorage"
},
"Buy": {
"dataSource": "vn"
},

View File

@ -0,0 +1,10 @@
{
"name": "EntryContainer",
"base": "Container",
"acls": [{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}]
}

View File

@ -0,0 +1,13 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
require('../methods/entry-dms/removeFile')(Self);
require('../methods/entry-dms/downloadFile')(Self);
require('../methods/entry-dms/uploadFile')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
return new UserError('This document already exists on this entry');
return err;
});
};

View File

@ -0,0 +1,36 @@
{
"name": "EntryDms",
"base": "VnModel",
"mixins": {
"Loggable": true
},
"options": {
"mysql": {
"table": "entryDms"
}
},
"allowedContentTypes": [
"image/png",
"image/jpeg",
"image/jpg"
],
"properties": {
"dmsFk": {
"type": "number",
"id": true,
"required": true
}
},
"relations": {
"entry": {
"type": "belongsTo",
"model": "Entry",
"foreignKey": "entryFk"
},
"dms": {
"type": "belongsTo",
"model": "Dms",
"foreignKey": "dmsFk"
}
}
}

View File

@ -48,23 +48,18 @@ module.exports = Self => {
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);
const dms = await Self.rawSql(`
SELECT d.id
FROM ticketDms td
JOIN dms d ON d.id = td.dmsFk
JOIN dmsType dt ON dt.id = d.dmsTypeFk
WHERE td.ticketFk = ?
AND dt.code = 'cmr'
`, [ticketId]);
if (!dms) throw new UserError('Cmr file does not exist');
if (!dms.length) throw new UserError('Cmr file does not exist');
const response = await models.Dms.downloadFile(ctx, dms.id);
const response = await models.Dms.downloadFile(ctx, dms[0].id);
const email = new Email('cmr', {
ticketId,

View File

@ -26,7 +26,8 @@ describe('Supplier newSupplier()', () => {
const options = {transaction: tx};
ctx.args = {
name: 'NEWSUPPLIER',
nif: '12345678Z'
nif: '12345678Z',
city: 'Gotham'
};
const result = await models.Supplier.newSupplier(ctx, options);

View File

@ -19,7 +19,7 @@ module.exports = Self => {
type: 'any'
}, {
arg: 'phone',
type: 'string'
type: 'any'
}, {
arg: 'sageTaxTypeFk',
type: 'any'
@ -46,10 +46,10 @@ module.exports = Self => {
type: 'any'
}, {
arg: 'supplierActivityFk',
type: 'string'
type: 'any'
}, {
arg: 'healthRegister',
type: 'string'
type: 'any'
}, {
arg: 'isVies',
type: 'boolean'

View File

@ -129,10 +129,13 @@ describe('loopback model Supplier', () => {
const options = {transaction: tx};
try {
const newSupplier = await models.Supplier.create({name: 'ALFRED PENNYWORTH'}, options);
const fetchedSupplier = await models.Supplier.findById(newSupplier.id, null, options);
const newSupplier = {
name: 'ALFRED PENNYWORTH', nif: '87805752D', city: 'Gotham'
};
const supplierCreated = await models.Supplier.create(newSupplier, options);
const fetchedSupplier = await models.Supplier.findById(supplierCreated.id, null, options);
expect(Number(fetchedSupplier.account)).toEqual(4100000000 + newSupplier.id);
expect(Number(fetchedSupplier.account)).toEqual(4100000000 + supplierCreated.id);
await tx.rollback();
} catch (e) {
await tx.rollback();

View File

@ -17,17 +17,13 @@ module.exports = Self => {
message: 'The social name cannot be empty'
});
if (this.city) {
Self.validatesPresenceOf('city', {
message: 'City cannot be empty'
});
}
Self.validatesPresenceOf('city', {
message: 'City cannot be empty'
});
if (this.nif) {
Self.validatesPresenceOf('nif', {
message: 'The nif cannot be empty'
});
}
Self.validatesPresenceOf('nif', {
message: 'The nif cannot be empty'
});
Self.validatesUniquenessOf('nif', {
message: 'TIN must be unique'

View File

@ -19,6 +19,7 @@ module.exports = Self => {
FROM ticketTracking tt
WHERE tt.userFk = ?
GROUP BY ticketFk
ORDER BY created DESC
LIMIT 5;`;
return await Self.rawSql(query, [userId]);
};

View File

@ -42,18 +42,15 @@ module.exports = Self => {
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);
const hasDmsCmr = await Self.rawSql(`
SELECT d.id
FROM ticketDms td
JOIN dms d ON d.id = td.dmsFk
WHERE td.ticketFk = ?
AND d.dmsTypeFk = ?
`, [ticketId, dmsTypeCmr.id]);
if (hasDmsCmr?.dms())
if (hasDmsCmr.length)
throw new UserError('This ticket already has a cmr saved');
ctx.args.id = ticket.cmrFk;
@ -66,7 +63,7 @@ module.exports = Self => {
warehouseFk: ticket.warehouseFk,
reference: ticket.id,
contentType: 'application/pdf',
hasFile: true
hasFile: false
};
const dms = await models.Dms.createFromStream(data, 'pdf', pdfStream, myOptions);

View File

@ -33,8 +33,10 @@ module.exports = Self => {
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
let tx;
let ticket;
let dms;
let gestDocCreated = false;
let isSignUploaded;
let externalTickets = [];
if (typeof options == 'object')
Object.assign(myOptions, options);
@ -44,6 +46,11 @@ module.exports = Self => {
myOptions.transaction = tx;
}
const dmsTypeTicket = await models.DmsType.findOne({
where: {code: 'ticket'},
fields: ['id']
});
async function setLocation(ticketId) {
await models.Delivery.create({
ticketFk: ticketId,
@ -53,102 +60,105 @@ module.exports = Self => {
}, myOptions);
}
async function gestDocExists(ticketId) {
const ticketDms = await models.TicketDms.findOne({
where: {ticketFk: ticketId},
fields: ['dmsFk']
}, myOptions);
async function hasSignDms(ticketId) {
const hasTicketDms = await Self.rawSql(`
SELECT d.id
FROM ticketDms td
JOIN dms d ON d.id = td.dmsFk
WHERE td.ticketFk = ?
AND d.dmsTypeFk = ?
`, [ticketId, dmsTypeTicket.id], myOptions);
if (!ticketDms) return false;
const ticket = await models.Ticket.findById(ticketId, {fields: ['isSigned']}, myOptions);
if (ticket.isSigned == true)
return true;
else
await models.Dms.destroyAll({where: {reference: ticketId}}, myOptions);
return false;
if (hasTicketDms.length) return true;
}
async function createGestDoc(id) {
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);
async function createGestDoc() {
const ctxUploadFile = Object.assign({}, ctx);
if (ticket.route() === null)
throw new UserError('Ticket without route');
ctxUploadFile.args = {
warehouseId: ticket.warehouseFk,
companyId: ticket.companyFk,
dmsTypeId: dmsType.id,
reference: '',
dmsTypeId: dmsTypeTicket.id,
reference: ticket.id,
description: `Firma del cliente - Ruta ${ticket.route().id}`,
hasFile: false
};
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
gestDocCreated = true;
// Si se ha subido ya la firma, no se vuelve a subir, ya que si no
// da un error de deadlock en la db
isSignUploaded = true;
}
try {
for (const ticketId of tickets) {
const ticketState = await models.TicketState.findOne(
{where: {ticketFk: ticketId},
fields: ['alertLevel']
}, myOptions);
ticket = await models.Ticket.findById(ticketId, {
include: [{
relation: 'address',
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']
}, myOptions);
if (!ticketState)
throw new UserError('Ticket does not exist');
if (!ticket.route())
throw new UserError('Ticket without route');
if (ticketState.alertLevel < packedAlertLevel.id)
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');
if (location) await setLocation(ticketId);
if (!gestDocCreated) await createGestDoc(ticketId);
await models.TicketDms.create({ticketFk: ticketId, dmsFk: dms[0].id}, myOptions);
const ticket = await models.Ticket.findById(ticketId, null, myOptions);
if (!await hasSignDms(ticketId) && !isSignUploaded)
await createGestDoc(ticketId);
if (isSignUploaded)
await models.TicketDms.create({ticketFk: ticket.id, dmsFk: dms[0].id}, myOptions);
await ticket.updateAttribute('isSigned', true, myOptions);
const deliveryState = await models.State.findOne({
where: {
code: 'DELIVERED'
}
where: {code: 'DELIVERED'}
}, myOptions);
await models.Ticket.state(ctx, {
ticketFk: ticketId,
stateFk: deliveryState.id
}, myOptions);
}
if (ticket?.address()?.province()?.country()?.code != 'ES' && ticket.cmrFk) {
await models.Ticket.saveCmr(ctx, [ticketId], myOptions);
externalTickets.push(ticketId);
}
}
if (tx) await tx.commit();
return;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
await models.Route.cmrEmail(ctx, externalTickets);
};
};

View File

@ -1,6 +1,6 @@
{
"name": "salix-back",
"version": "24.10.0",
"version": "24.12.0",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",

View File

@ -3786,7 +3786,7 @@ packages:
resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==}
dependencies:
base64-js: 1.5.1
ieee754: 1.1.13
ieee754: 1.2.1
isarray: 1.0.0
dev: false
@ -7288,8 +7288,8 @@ packages:
- supports-color
dev: true
/http-proxy-agent@7.0.0:
resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
/http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
@ -7367,8 +7367,8 @@ packages:
transitivePeerDependencies:
- supports-color
/https-proxy-agent@7.0.2:
resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==}
/https-proxy-agent@7.0.4:
resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
engines: {node: '>= 14'}
dependencies:
agent-base: 7.1.0
@ -9780,6 +9780,7 @@ packages:
/mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
requiresBuild: true
dev: false
/mkdirp@0.3.0:
@ -10692,8 +10693,8 @@ packages:
agent-base: 7.1.0
debug: 4.3.4(supports-color@6.1.0)
get-uri: 6.0.2
http-proxy-agent: 7.0.0
https-proxy-agent: 7.0.2
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
pac-resolver: 7.0.0
socks-proxy-agent: 8.0.2
transitivePeerDependencies:
@ -11155,8 +11156,8 @@ packages:
dependencies:
agent-base: 7.1.0
debug: 4.3.4(supports-color@6.1.0)
http-proxy-agent: 7.0.0
https-proxy-agent: 7.0.2
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
lru-cache: 7.18.3
pac-proxy-agent: 7.0.1
proxy-from-env: 1.1.0
@ -11288,6 +11289,7 @@ packages:
/queue-tick@1.0.1:
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
requiresBuild: true
dev: false
/quick-lru@4.0.1: