diff --git a/db/routines/vn/triggers/itemType_afterDelete.sql b/db/routines/vn/triggers/itemType_afterDelete.sql new file mode 100644 index 000000000..32a1ba31c --- /dev/null +++ b/db/routines/vn/triggers/itemType_afterDelete.sql @@ -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 ; diff --git a/db/routines/vn/triggers/itemType_beforeInsert.sql b/db/routines/vn/triggers/itemType_beforeInsert.sql new file mode 100644 index 000000000..d33d8fb56 --- /dev/null +++ b/db/routines/vn/triggers/itemType_beforeInsert.sql @@ -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 ; diff --git a/db/routines/vn/triggers/itemType_beforeUpdate.sql b/db/routines/vn/triggers/itemType_beforeUpdate.sql index 613ae8bfa..6a03d40d0 100644 --- a/db/routines/vn/triggers/itemType_beforeUpdate.sql +++ b/db/routines/vn/triggers/itemType_beforeUpdate.sql @@ -3,6 +3,7 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`itemType_beforeUpdate` BEFORE UPDATE ON `itemType` FOR EACH ROW BEGIN + SET NEW.editorFk = account.myUser_getId(); IF NEW.itemPackingTypeFk = '' THEN SET NEW.itemPackingTypeFk = NULL; diff --git a/db/routines/vn/triggers/productionConfig_afterDelete.sql b/db/routines/vn/triggers/productionConfig_afterDelete.sql index 6b6800d4f..bd485d590 100644 --- a/db/routines/vn/triggers/productionConfig_afterDelete.sql +++ b/db/routines/vn/triggers/productionConfig_afterDelete.sql @@ -3,7 +3,7 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`productionConfig_afterD AFTER DELETE ON `productionConfig` FOR EACH ROW BEGIN - INSERT INTO productionConfig + INSERT INTO productionConfigLog SET `action` = 'delete', `changedModel` = 'ProductionConfig', `changedModelId` = OLD.id, diff --git a/db/versions/11297-graySalal/00-firstScript.sql b/db/versions/11297-graySalal/00-firstScript.sql new file mode 100644 index 000000000..4d4711306 --- /dev/null +++ b/db/versions/11297-graySalal/00-firstScript.sql @@ -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'); diff --git a/db/versions/11297-graySalal/01-firstScript.sql b/db/versions/11297-graySalal/01-firstScript.sql new file mode 100644 index 000000000..78ce1a540 --- /dev/null +++ b/db/versions/11297-graySalal/01-firstScript.sql @@ -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; diff --git a/loopback/locale/en.json b/loopback/locale/en.json index c0b5c4386..ec99e246a 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -244,6 +244,6 @@ "There are tickets for this area, delete them first": "There are tickets for this area, delete them first", "Payment method is required": "Payment method is required", "You do not have permission to modify the booked field": "You do not have permission to modify the booked field", - "null": "null", - "Invalid or expired verification code": "Invalid or expired verification code" -} \ No newline at end of file + "Invalid or expired verification code": "Invalid or expired verification code", + "ticketLostExpedition": "The ticket [{{ticketId}}]({{{ticketUrl}}}) has the following lost expedition:{{ expeditionId }}" +} diff --git a/loopback/locale/es.json b/loopback/locale/es.json index ec7959450..077f2c350 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -387,9 +387,6 @@ "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", "You do not have permission to modify the booked field": "No tienes permisos para modificar el campo contabilizada", - "Entry 99 is not editable": "Entry 99 is not editable", - "Entry 9 is not editable": "Entry 9 is not editable", - "Entry must have lines to be marked booked": "Entry must have lines to be marked booked", - "Entry 10 is not editable": "Entry 10 is not editable", - "Entry 7 is not editable": "Entry 7 is not editable" -} \ No newline at end of file + "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" +} diff --git a/loopback/locale/fr.json b/loopback/locale/fr.json index 1fc25a112..9941358be 100644 --- a/loopback/locale/fr.json +++ b/loopback/locale/fr.json @@ -123,8 +123,8 @@ "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}}}", "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 quantity": "J'ai changé {{changes}} 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}}})", "Changes in sales": "la quantité de {{itemId}} {{concept}} de {{oldQuantity}} ➔ {{newQuantity}}", "State": "État", "regular": "normal", @@ -364,5 +364,7 @@ "Cannot send mail": "Impossible d'envoyer le mail", "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", - "You do not have permission to modify the booked field": "Vous n'avez pas la permission de modifier le champ comptabilisé" + "You do not have permission to modify the booked field": "Vous n'avez pas la permission de modifier le champ comptabilisé", + "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à" } diff --git a/loopback/locale/pt.json b/loopback/locale/pt.json index d9f00095d..e84b30f3d 100644 --- a/loopback/locale/pt.json +++ b/loopback/locale/pt.json @@ -124,7 +124,7 @@ "Changed sale discount": "Desconto da venda alterado no ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Created claim": "Reclamação criada [{{claimId}}]({{{claimUrl}}}) no ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed sale price": "Preço da venda alterado para [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* no ticket [{{ticketId}}]({{{ticketUrl}}})", - "Changed sale quantity": "Quantidade da venda alterada para {{changes}} no ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "Quantidade da venda alterada para {{changes}} no ticket [{{ticketId}}]({{{ticketUrl}}})", "Changes in sales": " [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* ", "State": "Estado", "regular": "normal", @@ -361,8 +361,9 @@ "It was not able to create the invoice": "Não foi possível criar a fatura", "The invoices have been created but the PDFs could not be generated": "Foi faturado, mas o PDF não pôde ser gerado", "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", - "The quantity claimed cannot be greater than the quantity of the line": "O valor reclamado não pode ser superior ao valor da linha", - "You do not have permission to modify the booked field": "Você não tem permissão para modificar o campo contabilizado" + "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." } diff --git a/modules/client/back/methods/client/createWithUser.js b/modules/client/back/methods/client/createWithUser.js index c8cd282e1..1d5e71fca 100644 --- a/modules/client/back/methods/client/createWithUser.js +++ b/modules/client/back/methods/client/createWithUser.js @@ -1,3 +1,4 @@ +/* eslint max-len: ["error", { "code": 150 }]*/ const UserError = require('vn-loopback/util/user-error'); module.exports = function(Self) { @@ -98,6 +99,8 @@ module.exports = function(Self) { return client; } catch (e) { 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; } }; diff --git a/modules/item/back/methods/item/filter.js b/modules/item/back/methods/item/filter.js index 909b3dff8..54dd975a4 100644 --- a/modules/item/back/methods/item/filter.js +++ b/modules/item/back/methods/item/filter.js @@ -38,13 +38,23 @@ module.exports = Self => { type: 'integer', description: 'Type id', }, + { + arg: 'producerFk', + type: 'integer', + description: 'Producer id', + }, + { + arg: 'instrastatFk', + type: 'string', + description: 'intrastat id', + }, { arg: 'isActive', type: 'boolean', description: 'Whether the item is or not active', }, { - arg: 'buyerFk', + arg: 'workerFk', type: 'integer', description: 'The buyer of the item', }, @@ -126,14 +136,16 @@ module.exports = Self => { return {'i.stemMultiplier': value}; case 'categoryFk': return {'ic.id': value}; - case 'buyerFk': + case 'workerFk': return {'it.workerFk': value}; + case 'producerFk': + return {'pr.id': value}; case 'supplierFk': return {'s.id': value}; case 'origin': return {'ori.code': value}; - case 'intrastat': - return {'intr.description': value}; + case 'intrastatFk': + return {'i.intrastatFk': value}; case 'landed': return {'lb.landed': value}; } @@ -172,6 +184,7 @@ module.exports = Self => { u.name AS userName, ori.code AS origin, ic.name AS category, + i.intrastatFk, intr.description AS intrastat, b.grouping, b.packing, diff --git a/modules/item/back/methods/item/specs/filter.spec.js b/modules/item/back/methods/item/specs/filter.spec.js index 14467d1d8..a8aaca786 100644 --- a/modules/item/back/methods/item/specs/filter.spec.js +++ b/modules/item/back/methods/item/specs/filter.spec.js @@ -86,7 +86,7 @@ describe('item filter()', () => { try { const filter = {}; - const ctx = {args: {filter: filter, buyerFk: 16}, req: {accessToken: {userId: 1}}}; + const ctx = {args: {filter: filter, workerFk: 16}, req: {accessToken: {userId: 1}}}; const result = await models.Item.filter(ctx, filter, options); expect(result.length).toEqual(2); diff --git a/modules/item/back/model-config.json b/modules/item/back/model-config.json index 5dbe4d62a..dcd973524 100644 --- a/modules/item/back/model-config.json +++ b/modules/item/back/model-config.json @@ -47,6 +47,9 @@ "ItemType": { "dataSource": "vn" }, + "ItemTypeLog": { + "dataSource": "vn" + }, "ItemTypeTag": { "dataSource": "vn" }, diff --git a/modules/item/back/models/item-type-log.json b/modules/item/back/models/item-type-log.json new file mode 100644 index 000000000..82a218aa6 --- /dev/null +++ b/modules/item/back/models/item-type-log.json @@ -0,0 +1,9 @@ +{ + "name": "ItemTypeLog", + "base": "Log", + "options": { + "mysql": { + "table": "itemTypeLog" + } + } +} diff --git a/modules/item/back/models/item-type.json b/modules/item/back/models/item-type.json index c5c920b2f..88f66899e 100644 --- a/modules/item/back/models/item-type.json +++ b/modules/item/back/models/item-type.json @@ -29,6 +29,12 @@ }, "isLaid": { "type": "boolean" + }, + "maxRefs": { + "type": "string" + }, + "isFragile": { + "type": "boolean" } }, "relations": { diff --git a/modules/shelving/back/models/parking.json b/modules/shelving/back/models/parking.json index 47a3305ae..a9393fc6a 100644 --- a/modules/shelving/back/models/parking.json +++ b/modules/shelving/back/models/parking.json @@ -38,5 +38,13 @@ "model": "Sector", "foreignKey": "sectorFk" } + }, + "scope": { + "include": { + "relation": "sector", + "scope": { + "fields": ["id", "description"] + } + } } } diff --git a/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js b/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js index 747352286..71bc453de 100644 --- a/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js +++ b/modules/ticket/back/methods/expedition-state/specs/addExpeditionState.spec.js @@ -2,6 +2,7 @@ const models = require('vn-loopback/server/server').models; describe('expeditionState addExpeditionState()', () => { const ctx = beforeAll.getCtx(); + beforeAll.mockLoopBackContext(); it('should update the expedition states', async() => { const tx = await models.ExpeditionState.beginTransaction({}); try { diff --git a/modules/ticket/back/methods/sale/recalculatePrice.js b/modules/ticket/back/methods/sale/recalculatePrice.js index fd3d6aa9b..ea71032d0 100644 --- a/modules/ticket/back/methods/sale/recalculatePrice.js +++ b/modules/ticket/back/methods/sale/recalculatePrice.js @@ -48,7 +48,7 @@ module.exports = Self => { CALL vn.sale_recalcComponent(null); 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(); diff --git a/modules/ticket/back/methods/sale/specs/updatePrice.spec.js b/modules/ticket/back/methods/sale/specs/updatePrice.spec.js index 9d1403df0..100f74bf0 100644 --- a/modules/ticket/back/methods/sale/specs/updatePrice.spec.js +++ b/modules/ticket/back/methods/sale/specs/updatePrice.spec.js @@ -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() => { const tx = await models.Sale.beginTransaction({}); diff --git a/modules/ticket/back/methods/sale/updatePrice.js b/modules/ticket/back/methods/sale/updatePrice.js index 191fd09e3..d4f128082 100644 --- a/modules/ticket/back/methods/sale/updatePrice.js +++ b/modules/ticket/back/methods/sale/updatePrice.js @@ -91,7 +91,21 @@ module.exports = Self => { value: componentValue }, 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.ticket_recalc(?, NULL)', [sale.ticketFk], myOptions); diff --git a/modules/ticket/back/models/expedition-state.js b/modules/ticket/back/models/expedition-state.js index 496dd88d3..f819dc05c 100644 --- a/modules/ticket/back/models/expedition-state.js +++ b/modules/ticket/back/models/expedition-state.js @@ -1,4 +1,66 @@ +const LoopBackContext = require('loopback-context'); + module.exports = function(Self) { require('../methods/expedition-state/filter')(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); + } + }); }; + diff --git a/modules/ticket/back/models/expedition-state.json b/modules/ticket/back/models/expedition-state.json index 159a9275e..522327031 100644 --- a/modules/ticket/back/models/expedition-state.json +++ b/modules/ticket/back/models/expedition-state.json @@ -19,7 +19,8 @@ "type": "number" }, "typeFk": { - "type": "number" + "type": "number", + "required": true }, "userFk": { "type": "number" diff --git a/modules/ticket/back/models/sale.json b/modules/ticket/back/models/sale.json index 96a36bbc9..947115f5c 100644 --- a/modules/ticket/back/models/sale.json +++ b/modules/ticket/back/models/sale.json @@ -28,6 +28,9 @@ "discount": { "type": "number" }, + "priceFixed": { + "type": "number" + }, "reserved": { "type": "boolean" }, diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json index c334c0d05..937df98c0 100644 --- a/modules/worker/back/models/worker.json +++ b/modules/worker/back/models/worker.json @@ -204,6 +204,15 @@ ] }, "summary": { + "fields": [ + "id", + "firstName", + "lastName", + "bossFk", + "sex", + "phone", + "mobileExtension" + ], "include": [ { "relation": "user", @@ -247,10 +256,21 @@ } }, { - "relation": "boss" + "relation": "boss", + "scope": { + "fields": [ + "id", + "name" + ] + } }, { - "relation": "client" + "relation": "client", + "scope": { + "fields": [ + "id" + ] + } }, { "relation": "sip", @@ -272,14 +292,16 @@ "fields": [ "id", "fiDueDate", - "sex", "seniority", "fi", "isFreelance", "isSsDiscounted", "hasMachineryAuthorized", "isDisable", - "birth" + "birth", + "educationLevelFk", + "originCountryFk", + "maritalStatus" ] } }