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

This commit is contained in:
Jon Elias 2024-10-28 09:52:17 +00:00
commit b27aa1bd94
22 changed files with 225 additions and 21 deletions

View File

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

View File

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

View File

@ -3,6 +3,7 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`itemType_beforeUpdate`
BEFORE UPDATE ON `itemType` BEFORE UPDATE ON `itemType`
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
SET NEW.editorFk = account.myUser_getId();
IF NEW.itemPackingTypeFk = '' THEN IF NEW.itemPackingTypeFk = '' THEN
SET NEW.itemPackingTypeFk = NULL; SET NEW.itemPackingTypeFk = NULL;

View File

@ -3,7 +3,7 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`productionConfig_afterD
AFTER DELETE ON `productionConfig` AFTER DELETE ON `productionConfig`
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
INSERT INTO productionConfig INSERT INTO productionConfigLog
SET `action` = 'delete', SET `action` = 'delete',
`changedModel` = 'ProductionConfig', `changedModel` = 'ProductionConfig',
`changedModelId` = OLD.id, `changedModelId` = OLD.id,

View File

@ -0,0 +1,27 @@
ALTER TABLE vn.itemType
ADD editorFk int(10) unsigned DEFAULT NULL NULL,
ADD CONSTRAINT itemType_user_FK FOREIGN KEY (editorFk) REFERENCES account.`user`(id);
CREATE TABLE `vn`.`itemTypeLog` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`originFk` int(11) DEFAULT NULL,
`userFk` int(10) unsigned DEFAULT NULL,
`action` set('insert','update','delete') NOT NULL,
`creationDate` timestamp NULL DEFAULT current_timestamp(),
`description` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL,
`changedModel` enum('ItemType') NOT NULL DEFAULT 'ItemType',
`oldInstance` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`oldInstance`)),
`newInstance` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`newInstance`)),
`changedModelId` int(11) NOT NULL,
`changedModelValue` varchar(45) DEFAULT NULL,
`summaryId` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `itemTypeLogUserFk_idx` (`userFk`),
KEY `itemTypeLog_changedModel` (`changedModel`,`changedModelId`,`creationDate`),
KEY `itemTypeLog_originFk` (`originFk`,`creationDate`),
KEY `itemTypeLog_creationDate_IDX` (`creationDate` DESC) USING BTREE,
CONSTRAINT `itemTypeLogUserFk` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci `PAGE_COMPRESSED`=1;
INSERT IGNORE INTO salix.ACL (model,property,principalId)
VALUES ('ItemTypeLog','find','employee');

View File

@ -0,0 +1,3 @@
ALTER TABLE vn.itemType
ADD CONSTRAINT itemType_itemPackingType_FK FOREIGN KEY (itemPackingTypeFk)
REFERENCES vn.itemPackingType(code) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -241,5 +241,9 @@
"The height must be greater than 50cm": "The height must be greater than 50cm", "The height must be greater than 50cm": "The height must be greater than 50cm",
"The maximum height of the wagon is 200cm": "The maximum height of the wagon is 200cm", "The maximum height of the wagon is 200cm": "The maximum height of the wagon is 200cm",
"The quantity claimed cannot be greater than the quantity of the line": "The quantity claimed cannot be greater than the quantity of the line", "The quantity claimed cannot be greater than the quantity of the line": "The quantity claimed cannot be greater than the quantity of the line",
"There are tickets for this area, delete them first": "There are tickets for this area, delete them first" "There are tickets for this area, delete them first": "There are tickets for this area, delete them first",
"ticketLostExpedition": "The ticket [{{ticketId}}]({{{ticketUrl}}}) has the following lost expedition:{{ expeditionId }}",
"null": "null",
"Invalid or expired verification code": "Invalid or expired verification code",
"Payment method is required": "Payment method is required"
} }

View File

@ -385,5 +385,7 @@
"The quantity claimed cannot be greater than the quantity of the line": "La cantidad reclamada no puede ser mayor que la cantidad de la línea", "The quantity claimed cannot be greater than the quantity of the line": "La cantidad reclamada no puede ser mayor que la cantidad de la línea",
"type cannot be blank": "Se debe rellenar el tipo", "type cannot be blank": "Se debe rellenar el tipo",
"There are tickets for this area, delete them first": "Hay tickets para esta sección, borralos primero", "There are tickets for this area, delete them first": "Hay tickets para esta sección, borralos primero",
"There is no company associated with that warehouse": "No hay ninguna empresa asociada a ese almacén" "There is no company associated with that warehouse": "No hay ninguna empresa asociada a ese almacén",
"ticketLostExpedition": "El ticket [{{ticketId}}]({{{ticketUrl}}}) tiene la siguiente expedición perdida:{{ expeditionId }}",
"The web user's email already exists": "El correo del usuario web ya existe"
} }

View File

@ -123,7 +123,7 @@
"Added sale to ticket": "J'ai ajouté la ligne suivante au ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", "Added sale to ticket": "J'ai ajouté la ligne suivante au ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "J'ai changé le rabais des lignes suivantes du ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed sale discount": "J'ai changé le rabais des lignes suivantes du ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "J'ai créé la réclamation [{{claimId}}]({{{claimUrl}}}) des lignes suivantes du ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Created claim": "J'ai créé la réclamation [{{claimId}}]({{{claimUrl}}}) des lignes suivantes du ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": " le prix de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* du ticket [{{ticketId}}]({{{ticketUrl}}})",, "Changed sale price": " le prix de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* du ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changed sale quantity": "J'ai changé {{changes}} du ticket [{{ticketId}}]({{{ticketUrl}}})", "Changed sale quantity": "J'ai changé {{changes}} du ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changes in sales": "la quantité de {{itemId}} {{concept}} de {{oldQuantity}} ➔ {{newQuantity}}", "Changes in sales": "la quantité de {{itemId}} {{concept}} de {{oldQuantity}} ➔ {{newQuantity}}",
"State": "État", "State": "État",
@ -363,5 +363,7 @@
"It has been invoiced but the PDF of refund not be generated": "Il a été facturé mais le PDF de remboursement n'a pas été généré", "It has been invoiced but the PDF of refund not be generated": "Il a été facturé mais le PDF de remboursement n'a pas été généré",
"Cannot send mail": "Impossible d'envoyer le mail", "Cannot send mail": "Impossible d'envoyer le mail",
"Original invoice not found": "Facture originale introuvable", "Original invoice not found": "Facture originale introuvable",
"The quantity claimed cannot be greater than the quantity of the line": "Le montant réclamé ne peut pas être supérieur au montant de la ligne" "The quantity claimed cannot be greater than the quantity of the line": "Le montant réclamé ne peut pas être supérieur au montant de la ligne",
"ticketLostExpedition": "Le ticket [{{ticketId}}]({{{ticketUrl}}}) a l'expédition perdue suivante : {{expeditionId}}",
"The web user's email already exists": "L'email de l'internaute existe déjà"
} }

View File

@ -363,5 +363,7 @@
"It has been invoiced but the PDF of refund not be generated": "Foi faturado mas não foi gerado o PDF do reembolso", "It has been invoiced but the PDF of refund not be generated": "Foi faturado mas não foi gerado o PDF do reembolso",
"Original invoice not found": "Fatura original não encontrada", "Original invoice not found": "Fatura original não encontrada",
"Cannot send mail": "Não é possível enviar o email", "Cannot send mail": "Não é possível enviar o email",
"The quantity claimed cannot be greater than the quantity of the line": "O valor reclamado não pode ser superior ao valor da linha" "The quantity claimed cannot be greater than the quantity of the line": "O valor reclamado não pode ser superior ao valor da linha",
"ticketLostExpedition": "O ticket [{{ticketId}}]({{{ticketUrl}}}) tem a seguinte expedição perdida: {{expeditionId}}",
"The web user's email already exists": "O e-mail do utilizador da web já existe."
} }

View File

@ -1,3 +1,4 @@
/* eslint max-len: ["error", { "code": 150 }]*/
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = function(Self) { module.exports = function(Self) {
@ -98,6 +99,8 @@ module.exports = function(Self) {
return client; return client;
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();
if (e.message && e.message.includes(`Email already exists`)) throw new UserError(`The web user's email already exists`);
throw e; throw e;
} }
}; };

View File

@ -47,6 +47,9 @@
"ItemType": { "ItemType": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ItemTypeLog": {
"dataSource": "vn"
},
"ItemTypeTag": { "ItemTypeTag": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,9 @@
{
"name": "ItemTypeLog",
"base": "Log",
"options": {
"mysql": {
"table": "itemTypeLog"
}
}
}

View File

@ -29,6 +29,12 @@
}, },
"isLaid": { "isLaid": {
"type": "boolean" "type": "boolean"
},
"maxRefs": {
"type": "string"
},
"isFragile": {
"type": "boolean"
} }
}, },
"relations": { "relations": {

View File

@ -2,6 +2,7 @@ const models = require('vn-loopback/server/server').models;
describe('expeditionState addExpeditionState()', () => { describe('expeditionState addExpeditionState()', () => {
const ctx = beforeAll.getCtx(); const ctx = beforeAll.getCtx();
beforeAll.mockLoopBackContext();
it('should update the expedition states', async() => { it('should update the expedition states', async() => {
const tx = await models.ExpeditionState.beginTransaction({}); const tx = await models.ExpeditionState.beginTransaction({});
try { try {

View File

@ -48,7 +48,7 @@ module.exports = Self => {
CALL vn.sale_recalcComponent(null); CALL vn.sale_recalcComponent(null);
DROP TEMPORARY TABLE tmp.recalculateSales;`; DROP TEMPORARY TABLE tmp.recalculateSales;`;
const recalculation = await Self.rawSql(query, salesIds, myOptions); const recalculation = await Self.rawSql(query, [salesIds], myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();

View File

@ -85,6 +85,25 @@ describe('sale updatePrice()', () => {
} }
}); });
it('should check if priceFixed has changed', async() => {
const tx = await models.Sale.beginTransaction({});
try {
const options = {transaction: tx};
const price = 3;
const beforeUpdate = await models.Sale.findById(saleId, null, options);
await models.Sale.updatePrice(ctx, saleId, price, options);
const afterUpdate = await models.Sale.findById(saleId, null, options);
expect(beforeUpdate.priceFixed).not.toEqual(afterUpdate.priceFixed);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => { it('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => {
const tx = await models.Sale.beginTransaction({}); const tx = await models.Sale.beginTransaction({});

View File

@ -91,7 +91,21 @@ module.exports = Self => {
value: componentValue value: componentValue
}, myOptions); }, myOptions);
} }
await sale.updateAttributes({price: newPrice}, myOptions);
const [priceFixed] = await Self.rawSql(`
SELECT SUM(value) value
FROM sale s
JOIN saleComponent sc ON sc.saleFk = s.id
JOIN component c ON c.id = sc.componentFk
JOIN componentType ct ON ct.id = c.typeFk
WHERE ct.isBase
AND s.id = ?
`, [id], myOptions);
await sale.updateAttributes({
price: newPrice,
priceFixed: priceFixed.value
}, myOptions);
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions); await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [sale.ticketFk], myOptions); await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [sale.ticketFk], myOptions);

View File

@ -1,4 +1,66 @@
const LoopBackContext = require('loopback-context');
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/expedition-state/filter')(Self); require('../methods/expedition-state/filter')(Self);
require('../methods/expedition-state/addExpeditionState')(Self); require('../methods/expedition-state/addExpeditionState')(Self);
Self.observe('before save', async ctx => {
const models = Self.app.models;
const changes = ctx.data || ctx.instance;
const instance = ctx.currentInstance;
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = {req: loopBackContext.active};
const httpRequest = httpCtx.req.http.req;
const $t = httpRequest.__;
const myOptions = {};
if (ctx.options && ctx.options.transaction)
myOptions.transaction = ctx.options.transaction;
const newStateType = changes?.typeFk;
if (newStateType == null) return;
const expeditionId = changes?.expeditionFk || instance?.expeditionFk;
const {code} = await models.ExpeditionStateType.findById(
newStateType,
{
fields: ['code']
},
myOptions);
if (code !== 'LOST') return;
const dataExpedition = await models.Expedition.findById(
expeditionId, {
fields: ['ticketFk'],
include: [{
relation: 'ticket',
scope: {
fields: ['clientFk'],
include: [{
relation: 'client',
scope: {
fields: ['name', 'salesPersonFk']
}
}]
}
}],
}, myOptions);
const salesPersonFk = dataExpedition.ticket()?.client()?.salesPersonFk;
if (salesPersonFk) {
const url = await Self.app.models.Url.getUrl();
const fullUrl = `${url}ticket/${dataExpedition.ticketFk}/expedition`;
const message = $t('ticketLostExpedition', {
ticketId: dataExpedition.ticketFk,
expeditionId: expeditionId,
url: fullUrl
});
await models.Chat.sendCheckingPresence(httpCtx, salesPersonFk, message);
}
});
}; };

View File

@ -19,7 +19,8 @@
"type": "number" "type": "number"
}, },
"typeFk": { "typeFk": {
"type": "number" "type": "number",
"required": true
}, },
"userFk": { "userFk": {
"type": "number" "type": "number"

View File

@ -28,6 +28,9 @@
"discount": { "discount": {
"type": "number" "type": "number"
}, },
"priceFixed": {
"type": "number"
},
"reserved": { "reserved": {
"type": "boolean" "type": "boolean"
}, },

View File

@ -204,6 +204,15 @@
] ]
}, },
"summary": { "summary": {
"fields": [
"id",
"firstName",
"lastName",
"bossFk",
"sex",
"phone",
"mobileExtension"
],
"include": [ "include": [
{ {
"relation": "user", "relation": "user",
@ -247,10 +256,21 @@
} }
}, },
{ {
"relation": "boss" "relation": "boss",
"scope": {
"fields": [
"id",
"name"
]
}
}, },
{ {
"relation": "client" "relation": "client",
"scope": {
"fields": [
"id"
]
}
}, },
{ {
"relation": "sip", "relation": "sip",
@ -272,14 +292,16 @@
"fields": [ "fields": [
"id", "id",
"fiDueDate", "fiDueDate",
"sex",
"seniority", "seniority",
"fi", "fi",
"isFreelance", "isFreelance",
"isSsDiscounted", "isSsDiscounted",
"hasMachineryAuthorized", "hasMachineryAuthorized",
"isDisable", "isDisable",
"birth" "birth",
"educationLevelFk",
"originCountryFk",
"maritalStatus"
] ]
} }
} }