diff --git a/Jenkinsfile b/Jenkinsfile index d3dbfeddb..1e8f3e87f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,6 +24,7 @@ node { FROM_GIT = env.JOB_NAME.startsWith('gitea/') RUN_TESTS = !PROTECTED_BRANCH && FROM_GIT RUN_BUILD = PROTECTED_BRANCH && FROM_GIT + // https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables echo "NODE_NAME: ${env.NODE_NAME}" echo "WORKSPACE: ${env.WORKSPACE}" @@ -206,9 +207,6 @@ pipeline { when { expression { FROM_GIT } } - environment { - DOCKER_HOST = "${env.SWARM_HOST}" - } steps { script { def packageJson = readJSON file: 'package.json' diff --git a/README.md b/README.md index 53478f425..b052bd8bf 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,16 @@ Required applications. * Node.js * Docker * Git +* MYT You will need to install globally the following items. ``` $ sudo npm install -g jest gulp-cli ``` +After installing MYT you will need the following item. +``` +$ apt install libkrb5-dev libssl-dev +``` ## Installing dependencies and launching diff --git a/back/methods/chat/sendCheckingPresence.js b/back/methods/chat/sendCheckingPresence.js index 85b66e94b..7ab5d63fe 100644 --- a/back/methods/chat/sendCheckingPresence.js +++ b/back/methods/chat/sendCheckingPresence.js @@ -1,3 +1,5 @@ +const isProduction = require('vn-loopback/server/boot/isProduction'); + module.exports = Self => { Self.remoteMethodCtx('sendCheckingPresence', { description: 'Creates a message in the chat model checking the user status', @@ -37,7 +39,7 @@ module.exports = Self => { if (!recipient) throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`); - if (process.env.NODE_ENV == 'test') + if (!isProduction()) message = `[Test:Environment to user ${userId}] ` + message; const chat = await models.Chat.create({ diff --git a/back/methods/chat/sendQueued.js b/back/methods/chat/sendQueued.js index 9a23af379..abda2ddc1 100644 --- a/back/methods/chat/sendQueued.js +++ b/back/methods/chat/sendQueued.js @@ -1,4 +1,6 @@ const axios = require('axios'); +const isProduction = require('vn-loopback/server/boot/isProduction'); + module.exports = Self => { Self.remoteMethodCtx('sendQueued', { description: 'Send a RocketChat message', @@ -94,7 +96,7 @@ module.exports = Self => { * @return {Promise} - The request promise */ Self.sendMessage = async function sendMessage(senderFk, recipient, message) { - if (process.env.NODE_ENV !== 'production') { + if (!isProduction(false)) { return new Promise(resolve => { return resolve({ statusCode: 200, @@ -149,7 +151,7 @@ module.exports = Self => { * @return {Promise} - The request promise */ Self.getUserStatus = async function getUserStatus(username) { - if (process.env.NODE_ENV !== 'production') { + if (!isProduction(false)) { return new Promise(resolve => { return resolve({ data: { diff --git a/back/methods/dms/deleteTrashFiles.js b/back/methods/dms/deleteTrashFiles.js index 239d654ef..e07f93c90 100644 --- a/back/methods/dms/deleteTrashFiles.js +++ b/back/methods/dms/deleteTrashFiles.js @@ -1,6 +1,7 @@ const UserError = require('vn-loopback/util/user-error'); const fs = require('fs-extra'); const path = require('path'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethod('deleteTrashFiles', { @@ -22,7 +23,7 @@ module.exports = Self => { if (typeof options == 'object') Object.assign(myOptions, options); - if (process.env.NODE_ENV == 'test') + if (!isProduction()) throw new UserError(`Action not allowed on the test environment`); const models = Self.app.models; diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 27be72295..0102911e0 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -1,5 +1,6 @@ const UserError = require('vn-loopback/util/user-error'); const axios = require('axios'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethodCtx('upload', { @@ -119,7 +120,7 @@ module.exports = Self => { ] }; - if (process.env.NODE_ENV != 'production') + if (!isProduction(false)) throw new UserError('Action not allowed on the test environment'); // delete old diff --git a/back/methods/image/scrub.js b/back/methods/image/scrub.js index 99c6bcbf3..3c83b3be7 100644 --- a/back/methods/image/scrub.js +++ b/back/methods/image/scrub.js @@ -1,6 +1,7 @@ const fs = require('fs-extra'); const path = require('path'); const UserError = require('vn-loopback/util/user-error'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethod('scrub', { @@ -43,8 +44,7 @@ module.exports = Self => { Self.scrub = async function(collection, remove, limit, dryRun, skipLock) { const $ = Self.app.models; - const env = process.env.NODE_ENV; - dryRun = dryRun || (env && env !== 'production'); + dryRun = dryRun || !isProduction(false); const instance = await $.ImageCollection.findOne({ fields: ['id'], diff --git a/back/methods/image/upload.js b/back/methods/image/upload.js index 51da327f6..b3cdfb88b 100644 --- a/back/methods/image/upload.js +++ b/back/methods/image/upload.js @@ -1,6 +1,7 @@ const UserError = require('vn-loopback/util/user-error'); const fs = require('fs/promises'); const path = require('path'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethodCtx('upload', { @@ -41,7 +42,7 @@ module.exports = Self => { if (!hasWriteRole) throw new UserError(`You don't have enough privileges`); - if (process.env.NODE_ENV == 'test') + if (!isProduction()) throw new UserError(`Action not allowed on the test environment`); // Upload file to temporary path diff --git a/back/methods/notification/send.js b/back/methods/notification/send.js index b2748477d..1bff7f686 100644 --- a/back/methods/notification/send.js +++ b/back/methods/notification/send.js @@ -1,4 +1,5 @@ const {Email} = require('vn-print'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethod('send', { @@ -70,7 +71,7 @@ module.exports = Self => { const newParams = Object.assign({}, queueParams, sendParams); const email = new Email(queueName, newParams); - if (process.env.NODE_ENV != 'test') + if (isProduction()) await email.send(); await queue.updateAttribute('status', statusSent); diff --git a/back/methods/vn-user/renew-token.js b/back/methods/vn-user/renew-token.js index 8e5ffc095..5581d19ac 100644 --- a/back/methods/vn-user/renew-token.js +++ b/back/methods/vn-user/renew-token.js @@ -18,15 +18,10 @@ module.exports = Self => { Self.renewToken = async function(ctx) { const {accessToken: token} = ctx.req; - // Check if current token is valid - - const {renewPeriod, courtesyTime} = await models.AccessTokenConfig.findOne({ - fields: ['renewPeriod', 'courtesyTime'] + const {courtesyTime} = await models.AccessTokenConfig.findOne({ + fields: ['courtesyTime'] }); - const now = Date.now(); - const differenceMilliseconds = now - token.created; - const differenceSeconds = Math.floor(differenceMilliseconds / 1000); - const isNotExceeded = differenceSeconds < renewPeriod - courtesyTime; + const isNotExceeded = await Self.validateToken(ctx); if (isNotExceeded) return token; diff --git a/back/methods/vn-user/validate-token.js b/back/methods/vn-user/validate-token.js new file mode 100644 index 000000000..3b75c7c34 --- /dev/null +++ b/back/methods/vn-user/validate-token.js @@ -0,0 +1,30 @@ +const {models} = require('vn-loopback/server/server'); +module.exports = Self => { + Self.remoteMethodCtx('validateToken', { + description: 'Validates the current logged user token', + accepts: [], + accessType: 'READ', + returns: { + type: 'Boolean', + root: true + }, + http: { + path: `/validateToken`, + verb: 'GET' + } + }); + + Self.validateToken = async function(ctx) { + const {accessToken: token} = ctx.req; + + // Check if current token is valid + const {renewPeriod, courtesyTime} = await models.AccessTokenConfig.findOne({ + fields: ['renewPeriod', 'courtesyTime'] + }); + const now = Date.now(); + const differenceMilliseconds = now - token.created; + const differenceSeconds = Math.floor(differenceMilliseconds / 1000); + const isNotExceeded = differenceSeconds < renewPeriod - courtesyTime; + return isNotExceeded; + }; +}; diff --git a/back/model-config.json b/back/model-config.json index e64386300..b643ab54f 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -186,5 +186,8 @@ }, "AgencyWorkCenter": { "dataSource": "vn" + }, + "RouteConfig": { + "dataSource": "vn" } } diff --git a/back/models/collection.json b/back/models/collection.json index cb8dc3d7c..8a8afeb89 100644 --- a/back/models/collection.json +++ b/back/models/collection.json @@ -1,6 +1,16 @@ { "name": "Collection", "base": "VnModel", + "properties": { + "id": { + "id": true, + "type": "number", + "required": true + }, + "workerFk": { + "type": "number" + } + }, "options": { "mysql": { "table": "collection" diff --git a/back/models/routeConfig.json b/back/models/routeConfig.json new file mode 100644 index 000000000..f3d929749 --- /dev/null +++ b/back/models/routeConfig.json @@ -0,0 +1,18 @@ +{ + "name": "RouteConfig", + "base": "VnModel", + "options": { + "mysql": { + "table": "routeConfig" + } + }, + "properties": { + "id": { + "type": "number", + "description": "Identifier" + }, + "kmMax": { + "type": "number" + } + } +} diff --git a/back/models/vn-user.js b/back/models/vn-user.js index b59f13ffa..d38fe5a92 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -15,6 +15,7 @@ module.exports = function(Self) { require('../methods/vn-user/renew-token')(Self); require('../methods/vn-user/share-token')(Self); require('../methods/vn-user/update-user')(Self); + require('../methods/vn-user/validate-token')(Self); Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create'); diff --git a/back/models/vn-user.json b/back/models/vn-user.json index 5f6ac3f47..8e3304085 100644 --- a/back/models/vn-user.json +++ b/back/models/vn-user.json @@ -113,6 +113,13 @@ "principalId": "$everyone", "permission": "ALLOW" }, + { + "property": "validateToken", + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$authenticated", + "permission": "ALLOW" + }, { "property": "privileges", "accessType": "*", diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 49cb17f0f..6818e7200 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -762,7 +762,12 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF (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, 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), - (33, 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); + (33, 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), + (34, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1103, 'BEJAR', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), + (35, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Somewhere in Philippines', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), + (36, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Ant-Man Adventure', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), + (37, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1110, 'Deadpool swords', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL); + INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) VALUES (1, 11, 1, 'ready'), @@ -808,7 +813,10 @@ INSERT INTO `vn`.`ticketTracking`(`ticketFk`, `stateFk`, `userFk`, `created`) (21, 1, 19, DATE_ADD(util.VN_NOW(), INTERVAL +1 MONTH)), (22, 1, 19, DATE_ADD(util.VN_NOW(), INTERVAL +1 MONTH)), (23, 16, 21, util.VN_NOW()), - (24, 16, 21, util.VN_NOW()); + (24, 16, 21, util.VN_NOW()), + (34, 14, 49, util.VN_NOW()), + (35, 14, 18, util.VN_NOW()), + (36, 14, 18, util.VN_NOW()); INSERT INTO `vn`.`deliveryPoint` (`id`, `name`, `ubication`) VALUES @@ -1068,7 +1076,10 @@ INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `pric (37, 4, 31, 'Melee weapon heavy shield 100cm', 20, 1.72, 0, 0, 0, util.VN_CURDATE()), (36, 4, 30, 'Melee weapon heavy shield 100cm', 20, 1.72, 0, 0, 0, util.VN_CURDATE()), (38, 2, 32, 'Melee weapon combat fist 15cm', 30, 7.07, 0, 0, 0, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)), - (39, 1, 32, 'Ranged weapon longbow 200cm', 2, 103.49, 0, 0, 0, util.VN_CURDATE()); + (39, 1, 32, 'Ranged weapon longbow 200cm', 2, 103.49, 0, 0, 0, util.VN_CURDATE()), + (40, 2, 34, 'Melee weapon combat fist 15cm', 10.00, 3.91, 0, 0, 0, util.VN_CURDATE()), + (41, 2, 35, 'Melee weapon combat fist 15cm', 8.00, 3.01, 0, 0, 0, util.VN_CURDATE()), + (42, 2, 36, 'Melee weapon combat fist 15cm', 6.00, 2.50, 0, 0, 0, util.VN_CURDATE()); INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`) VALUES @@ -1205,11 +1216,11 @@ INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`) (32, 36, -92.324), (32, 39, 0.994); -INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `visible`, `grouping`, `packing`, `userFk`) +INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `visible`, `grouping`, `packing`,`buyFk`, `userFk`) VALUES - (2, 'GVC', 1, 1, 1, 1106), - (4, 'HEJ', 1, 1, 1, 1106), - (1, 'UXN', 2, 12, 12, 1106); + (2, 'GVC', 1, 1, 1, 2,1106), + (4, 'HEJ', 1, 1, 1, NULL,1106), + (1, 'UXN', 2, 12, 12, NULL,1106); INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`) VALUES @@ -1247,14 +1258,20 @@ INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPacki INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`) VALUES (1, 1106, 5, DATE_ADD(util.VN_CURDATE(),INTERVAL +1 DAY), 1), - (2, 1106, 14, util.VN_CURDATE(), 1); + (2, 1106, 14, util.VN_CURDATE(), 1), + (4, 49, 5, util.VN_CURDATE(), 1), + (5, 18, 5, util.VN_CURDATE(), 1), + (6, 18, 5, util.VN_CURDATE(), 1); INSERT INTO `vn`.`ticketCollection`(`ticketFk`, `collectionFk`, `level`) VALUES (1, 1, 1), (2, 1, NULL), (3, 2, NULL), - (23, 1, NULL); + (23, 1, NULL), + (34, 4, 1), + (35, 5, 1), + (8, 6, 1); INSERT INTO `vn`.`genus`(`id`, `name`) VALUES @@ -3705,7 +3722,8 @@ INSERT IGNORE INTO vn.saleGroup SET id = 4, userFk = 1, parkingFk = 9, - sectorFk = 9992; + sectorFk = 9992, + ticketFk = 36; INSERT IGNORE INTO vn.sectorCollectionSaleGroup SET id = 9999, @@ -3807,3 +3825,27 @@ INSERT INTO `vn`.`ledgerCompany` SET INSERT INTO `vn`.`ledgerConfig` SET maxTolerance = 0.01; + +INSERT INTO vn.sectorCollection + SET id = 2, + userFk = 18, + sectorFk = 1; + +INSERT INTO vn.sectorCollectionSaleGroup + SET id = 8, + sectorCollectionFk = 2, + saleGroupFk = 4; + +INSERT INTO vn.saleGroup (userFk, parkingFk, sectorFk, ticketFk) + VALUES + (1, 1, 1, 37); + +INSERT INTO vn.sectorCollection + SET id = 3, + userFk = 18, + sectorFk = 1; + +INSERT INTO vn.sectorCollectionSaleGroup + SET id = 9, + sectorCollectionFk = 3, + saleGroupFk = 6; \ No newline at end of file diff --git a/db/routines/edi/procedures/ekt_scan.sql b/db/routines/edi/procedures/ekt_scan.sql index b0b75a6a7..0cf8bb466 100644 --- a/db/routines/edi/procedures/ekt_scan.sql +++ b/db/routines/edi/procedures/ekt_scan.sql @@ -23,42 +23,39 @@ BEGIN DECLARE vXtraLongAgj INT; DECLARE vDefaultKlo INT; - SELECT - ec.usefulAuctionLeftSegmentLength, - ec.standardBarcodeLength, - ec.floridayBarcodeLength, - ec.floramondoBarcodeLength, - ec.defaultKlo - INTO - vUsefulAuctionLeftSegmentLength, + SELECT usefulAuctionLeftSegmentLength, + standardBarcodeLength, + floridayBarcodeLength, + floramondoBarcodeLength, + defaultKlo + INTO vUsefulAuctionLeftSegmentLength, vStandardBarcodeLength, vFloridayBarcodeLength, vFloramondoBarcodeLength, vDefaultKlo - FROM edi.ektConfig ec; + FROM ektConfig; - DROP TEMPORARY TABLE IF EXISTS tmp.ekt; - CREATE TEMPORARY TABLE tmp.ekt + CREATE OR REPLACE TEMPORARY TABLE tmp.ekt ENGINE = MEMORY SELECT id ektFk FROM ekt LIMIT 0; - CASE + CASE WHEN LENGTH(vBarcode) <= vFloridayBarcodeLength THEN INSERT INTO tmp.ekt SELECT id - FROM edi.ektRecent e + FROM ektRecent e WHERE e.cps = vBarcode OR e.batchNumber = vBarcode; WHEN LENGTH(vBarcode) = vFloramondoBarcodeLength THEN INSERT INTO tmp.ekt SELECT e.id - FROM edi.ektRecent e + FROM ektRecent e WHERE e.pro = MID(vBarcode,2,6) - AND CAST(e.ptd AS SIGNED) = MID(vBarcode,8,5); + AND CAST(e.ptd AS SIGNED) = MID(vBarcode, 8, 5); ELSE - SET vBarcode = LPAD(vBarcode,vStandardBarcodeLength,'0'); + SET vBarcode = LPAD(vBarcode, vStandardBarcodeLength, '0'); SET vAuction = MID(vBarcode, 1, 3); SET vKlo = MID(vBarcode, 4, 2); SET vFec = MAKEDATE(YEAR(util.VN_CURDATE()), MID(vBarcode, 6, 3)); @@ -69,21 +66,23 @@ BEGIN -- Clásico de subasta -- Trade standard -- Trade que construye como la subasta - -- Trade como el anterior pero sin trade code + -- Trade como el anterior pero sin trade code INSERT INTO tmp.ekt SELECT id FROM ekt WHERE fec >= vFec - INTERVAL 1 DAY - AND (( - vKlo = vDefaultKlo + AND ( + (vKlo = vDefaultKlo AND (klo = vKlo OR klo IS NULL OR klo = 0) - AND agj IN (vShortAgj, vLongAgj, vXtraLongAgj)) - OR (klo = vKlo + AND agj IN (vShortAgj, vLongAgj, vXtraLongAgj) + ) OR ( + klo = vKlo AND auction = vAuction - AND agj = vShortAgj) + AND agj = vShortAgj + ) ) - ORDER BY agj DESC, fec DESC - LIMIT 1; + ORDER BY agj DESC, fec DESC + LIMIT 1; SELECT COUNT(*) FROM tmp.ekt INTO vIsFound; @@ -91,9 +90,11 @@ BEGIN IF NOT vIsFound THEN INSERT INTO tmp.ekt SELECT id - FROM edi.ektRecent e - WHERE e.batchNumber - = LEFT(vBarcode,vUsefulAuctionLeftSegmentLength) + FROM ektRecent e + WHERE e.batchNumber = LEFT( + vBarcode, + vUsefulAuctionLeftSegmentLength + ) AND e.batchNumber > 0; SELECT COUNT(*) FROM tmp.ekt INTO vIsFound; @@ -103,7 +104,7 @@ BEGIN IF NOT vIsFound THEN INSERT INTO tmp.ekt SELECT id - FROM edi.ektRecent e + FROM ektRecent e WHERE e.putOrderFk = vBarcode; SELECT COUNT(*) FROM tmp.ekt INTO vIsFound; @@ -113,18 +114,28 @@ BEGIN IF NOT vIsFound THEN INSERT INTO tmp.ekt SELECT id - FROM edi.ektRecent e - WHERE e.deliveryNumber - = MID(vBarcode, 4, 13) + FROM ektRecent e + WHERE e.deliveryNumber = MID(vBarcode, 4, 13) AND e.deliveryNumber > 0; SELECT COUNT(*) FROM tmp.ekt INTO vIsFound; END IF; + + -- Solo campo agj + IF NOT vIsFound THEN + INSERT INTO tmp.ekt + SELECT id + FROM ektRecent + WHERE agj = vShortAgj; + + SELECT COUNT(*) FROM tmp.ekt INTO vIsFound; + END IF; + END CASE; IF vIsFound THEN UPDATE ekt e - JOIN tmp.ekt t ON t.ektFk = e.id + JOIN tmp.ekt t ON t.ektFk = e.id SET e.scanned = TRUE; END IF; END$$ diff --git a/db/routines/floranet/procedures/catalogue_get.sql b/db/routines/floranet/procedures/catalogue_get.sql index 32624f383..7ce32cfac 100644 --- a/db/routines/floranet/procedures/catalogue_get.sql +++ b/db/routines/floranet/procedures/catalogue_get.sql @@ -40,19 +40,28 @@ proc:BEGIN postalCode, `type`, image, - description + description, + addressFk ) - SELECT i.name, - i.`size`, - i.id, + SELECT CONCAT(i.name, ' by ',a.nickname), + r.price + apc.deliveryCost, + r.itemFk, vLanded, vPostalCode, it.name, CONCAT('https://cdn.verdnatura.es/image/catalog/1600x900/', i.image), - i.description + i.description, + apc.addressFk FROM vn.item i + JOIN (SELECT itemFk, SUM(quantity * cost) price + FROM recipe + GROUP BY itemFk) r ON r.itemFk = i.id JOIN vn.itemType it ON it.id = i.typeFk - WHERE it.code IN ('FNR','FNP'); + JOIN addressPostCode apc + ON apc.dayOfWeek = dayOfWeek(vLanded) + AND NOW() < vLanded - INTERVAL apc.hoursInAdvance HOUR + AND apc.postCode = vPostalCode + JOIN vn.address a ON a.id = apc.addressFk; SELECT * FROM catalogue diff --git a/db/routines/floranet/procedures/contact_request.sql b/db/routines/floranet/procedures/contact_request.sql index 2ca25b87d..2132a86fc 100644 --- a/db/routines/floranet/procedures/contact_request.sql +++ b/db/routines/floranet/procedures/contact_request.sql @@ -13,8 +13,17 @@ BEGIN /** * Set actions for contact request * - * @param vPostalCode Delivery address postal code + * @param vName Name + * @param vPhone Phone number + * @param vEmail e-mail + * @param vMessage text of the message */ - + + CALL vn.mail_insert( + 'floranet@verdnatura.es', + vEmail, + 'Contact request', + CONCAT('Phone: ',vPhone, ' Message: ', vMessage) + ); END$$ DELIMITER ; \ No newline at end of file diff --git a/db/routines/floranet/procedures/deliveryDate_get.sql b/db/routines/floranet/procedures/deliveryDate_get.sql index 75e9d6257..a235e8c31 100644 --- a/db/routines/floranet/procedures/deliveryDate_get.sql +++ b/db/routines/floranet/procedures/deliveryDate_get.sql @@ -21,7 +21,7 @@ BEGIN apc.dayOfWeek - vCurrentDayOfWeek, 7 - apc.dayOfWeek ) DAY nextDay, - NOW() + INTERVAL apc.hoursInAdvance - 12 HOUR minDeliveryTime + NOW() + INTERVAL apc.hoursInAdvance HOUR minDeliveryTime FROM addressPostCode apc WHERE apc.postCode = vPostalCode HAVING nextDay > minDeliveryTime) sub; diff --git a/db/routines/floranet/procedures/order_confirm.sql b/db/routines/floranet/procedures/order_confirm.sql index b6aec033d..3b9413da9 100644 --- a/db/routines/floranet/procedures/order_confirm.sql +++ b/db/routines/floranet/procedures/order_confirm.sql @@ -1,25 +1,167 @@ -DROP PROCEDURE IF EXISTS floranet.order_confirm; - DELIMITER $$ $$ -CREATE DEFINER=`root`@`localhost`PROCEDURE floranet.order_confirm(vCatalogueFk INT) +CREATE OR REPLACE DEFINER=`root`@`localhost`PROCEDURE floranet.order_confirm(vCatalogueFk INT) READS SQL DATA -BEGIN -/** Update order.isPaid field. +proc:BEGIN +/** Update order.isPaid field, and makes the ticket * * @param vCatalogueFk floranet.catalogue.id * * @returns floranet.order.isPaid */ + DECLARE vNewTicketFk INT; + DECLARE vCustomerEmail VARCHAR(255); + DECLARE vFloranetEmail VARCHAR(255); + DECLARE vSubjectEmail VARCHAR(100); + DECLARE vBodyEmail TEXT; + DECLARE vZoneFk INT; + + DECLARE exit handler FOR SQLEXCEPTION + BEGIN + ROLLBACK; + + GET DIAGNOSTICS CONDITION 2 @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT; + + SELECT CONCAT('ERROR ', IFNULL(@errno,0), ': ', ifnull(@text,'texto')) AS `SQLEXCEPTION`; + + CALL vn.mail_insert( + 'floranet@verdnatura.es,pako@verdnatura.es', + 'noreply@verdnatura.es', + 'Floranet.order_confirm failure', + CONCAT('CatalogueFk: ', vCatalogueFk, '\n','ERROR ', IFNULL(@errno, 0), ': ', ifnull(@text, 'texto')) + ); + END; + + IF (SELECT isPaid FROM `order` WHERE catalogueFk = vCatalogueFk) THEN + SELECT CONCAT('CatalogueFk: ', vCatalogueFk, ' Esta orden ya está confirmada') AS `ERROR`; + LEAVE proc; + END IF; + + START TRANSACTION; + UPDATE `order` SET isPaid = TRUE, payed = NOW() WHERE catalogueFk = vCatalogueFk; - SELECT isPaid + SELECT zoneFk + INTO vZoneFk + FROM ( + SELECT zoneFk, COUNT(*) totalCount + FROM vn.ticket t + JOIN catalogue c ON c.id = vCatalogueFk + WHERE t.shipped > util.VN_CURDATE() - INTERVAL 1 YEAR + AND t.addressFk = c.addressFk + GROUP BY zoneFk + ORDER BY totalCount DESC + LIMIT 10000000000000000000 + ) sub + LIMIT 1; + + INSERT INTO vn.ticket ( + clientFk, + shipped, + addressFk, + agencyModeFk, + nickname, + warehouseFk, + routeFk, + companyFk, + landed, + zoneFk + ) + SELECT a.clientFk, + c.dated - INTERVAL 1 DAY, + c.addressFk, + a.agencyModeFk, + a.nickname, + ag.warehouseFk, + NULL, + co.id, + c.dated, + vZoneFk + FROM vn.address a + JOIN vn.agencyMode am ON am.id = a.agencyModeFk + JOIN vn.agency ag ON ag.id = am.agencyFk + JOIN catalogue c ON c.addressFk = a.id + JOIN vn.company co ON co.code = 'VNL' + WHERE c.id = vCatalogueFk; + + SET vNewTicketFk = LAST_INSERT_ID(); + + INSERT INTO vn.sale( + ticketFk, + itemFk, + concept, + price, + quantity) + SELECT + vNewTicketFk, + c.itemFk, + CONCAT('Entrega: ',c.name), + - c.price, + 1 + FROM catalogue c + JOIN addressPostCode apc + ON apc.addressFk = c.addressFk + AND apc.dayOfWeek = dayOfWeek(c.dated) + WHERE c.id = vCatalogueFk; + + INSERT INTO vn.sale( + ticketFk, + itemFk, + concept, + price, + quantity) + SELECT + vNewTicketFk, + r.elementFk, + i.longName, + r.cost, + r.quantity + FROM catalogue c + JOIN recipe r ON r.itemFk = c.itemFk + JOIN vn.item i ON i.id = r.elementFk + WHERE c.id = vCatalogueFk; + + SELECT cl.email, + cf.email, + CONCAT('Nuevo pedido FLORANET para entrega el ',c.dated), + CONCAT_WS('\n', + CONCAT('Producto: ', c.name), + CONCAT('Fecha de entrega: ',c.dated), + CONCAT('Destinatario: ', o.deliveryName), + CONCAT('Dirección: ', o.address), + CONCAT('CP: ', c.postalCode), + CONCAT('Foto: ', c.image), + CONCAT('Mensaje: ', IFNULL(o.message,"Ninguno.")), + CONCAT('Teléfono: ',IFNULL(o.deliveryPhone,"--")), + CONCAT('Observaciones: ', IFNULL(o.observations,"No hay.")) + ) + INTO vCustomerEmail, + vFloranetEmail, + vSubjectEmail, + vBodyEmail + FROM vn.client cl + JOIN vn.address a ON a.clientFk = cl.id + JOIN catalogue c ON c.addressFk = a.id + JOIN `order` o ON o.catalogueFk = c.id + JOIN config cf + WHERE c.id = vCatalogueFk; + + CALL vn.mail_insert( + vCustomerEmail, + vFloranetEmail, + vSubjectEmail, + vBodyEmail); + + SELECT isPaid, vNewTicketFk FROM `order` WHERE catalogueFk = vCatalogueFk; + + COMMIT; + END$$ DELIMITER ; \ No newline at end of file diff --git a/db/routines/floranet/procedures/order_put.sql b/db/routines/floranet/procedures/order_put.sql index 979588f8f..c5eb71472 100644 --- a/db/routines/floranet/procedures/order_put.sql +++ b/db/routines/floranet/procedures/order_put.sql @@ -7,7 +7,7 @@ BEGIN * * @param vJsonData The order data in json format */ - INSERT INTO `order` + REPLACE `order` SET catalogueFk = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.products[0].id')), customerName = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.customerName')), email = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.email')), @@ -15,7 +15,8 @@ BEGIN message= JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.message')), deliveryName = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.deliveryName')), address = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.address')), - deliveryPhone = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.deliveryPhone')); + deliveryPhone = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.deliveryPhone')), + observations = JSON_UNQUOTE(JSON_EXTRACT(vJsonData, '$.customer.customerData.observations')); SELECT LAST_INSERT_ID() orderFk; END$$ diff --git a/db/routines/floranet/procedures/sliders_get.sql b/db/routines/floranet/procedures/sliders_get.sql index 0e4aa297a..bafda4732 100644 --- a/db/routines/floranet/procedures/sliders_get.sql +++ b/db/routines/floranet/procedures/sliders_get.sql @@ -9,12 +9,11 @@ BEGIN * Returns list of url for sliders. */ SELECT - CONCAT('https://cdn.verdnatura.es/image/catalog/1600x900/', i.image) url, - i.longName - FROM vn.item i - JOIN vn.itemType it ON it.id = i.typeFk - WHERE it.code IN ('FNR','FNP') - LIMIT 3; - + CONCAT('https://cdn.verdnatura.es/image/catalog/1600x900/', i.image) url, + i.longName + FROM vn.item i + JOIN vn.itemType it ON it.id = i.typeFk + WHERE it.code IN ('FNR','FNP') + LIMIT 3; END$$ DELIMITER ; \ No newline at end of file diff --git a/db/routines/vn/procedures/itemShelvingTransfer.sql b/db/routines/vn/procedures/itemShelvingTransfer.sql index 326f8108b..ae82bfe9d 100644 --- a/db/routines/vn/procedures/itemShelvingTransfer.sql +++ b/db/routines/vn/procedures/itemShelvingTransfer.sql @@ -17,13 +17,15 @@ BEGIN SELECT itemFk, packing, - created + created, + buyFk FROM itemShelving WHERE id = vItemShelvingFk ) ish2 ON ish2.itemFk = ish.itemFk AND ish2.packing = ish.packing AND date(ish2.created) = date(ish.created) + AND ish2.buyFk = ish.buyFk WHERE ish.shelvingFk = vShelvingFk COLLATE utf8_unicode_ci; IF vNewItemShelvingFk THEN diff --git a/db/routines/vn/procedures/itemShelving_add.sql b/db/routines/vn/procedures/itemShelving_add.sql index d3e0f6a90..9395f5d80 100644 --- a/db/routines/vn/procedures/itemShelving_add.sql +++ b/db/routines/vn/procedures/itemShelving_add.sql @@ -2,7 +2,6 @@ DELIMITER $$ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_add`(IN vShelvingFk VARCHAR(8), IN vBarcode VARCHAR(22), IN vQuantity INT, IN vPackagingFk VARCHAR(10), IN vGrouping INT, IN vPacking INT, IN vWarehouseFk INT) BEGIN - /** * Añade registro o lo actualiza si ya existe. * @@ -15,11 +14,23 @@ BEGIN * @param vWarehouseFk indica el sector * **/ - DECLARE vItemFk INT; + DECLARE vBuyFk INT; + + SELECT id INTO vBuyFk + FROM buy WHERE id = vBarcode; SELECT barcodeToItem(vBarcode) INTO vItemFk; + IF vBuyFk IS NULL THEN + CALL cache.last_buy_refresh(FALSE); + + SELECT buy_id INTO vBuyFk + FROM cache.last_buy + WHERE item_id = vItemFk + AND warehouse_id = vWarehouseFk; + END IF; + IF vPacking IS NULL THEN SET vPacking = itemPacking(vBarcode, vWarehouseFk); @@ -29,31 +40,32 @@ BEGIN IF (SELECT COUNT(*) FROM itemShelving WHERE shelvingFk COLLATE utf8_unicode_ci = vShelvingFk AND itemFk = vItemFk - AND packing = vPacking) = 1 THEN + AND packing = vPacking + AND buyFk = vBuyFk) THEN UPDATE itemShelving - SET visible = visible+vQuantity + SET visible = visible + vQuantity WHERE shelvingFk COLLATE utf8_unicode_ci = vShelvingFk AND itemFk = vItemFk AND packing = vPacking; ELSE - CALL cache.last_buy_refresh(FALSE); - INSERT INTO itemShelving( itemFk, - shelvingFk, - visible, - grouping, - packing, - packagingFk) - SELECT vItemFk, - vShelvingFk, - vQuantity, - IFNULL(vGrouping, b.grouping), - IFNULL(vPacking, b.packing), - IFNULL(vPackagingFk, b.packagingFk) - FROM item i - LEFT JOIN cache.last_buy lb ON i.id = lb.item_id AND lb.warehouse_id = vWarehouseFk - LEFT JOIN buy b ON b.id = lb.buy_id - WHERE i.id = vItemFk; + INSERT INTO itemShelving( + itemFk, + shelvingFk, + visible, + grouping, + packing, + packagingFk, + buyFk) + SELECT vItemFk, + vShelvingFk, + vQuantity, + IFNULL(vGrouping, b.grouping), + IFNULL(vPacking, b.packing), + IFNULL(vPackagingFk, b.packagingFk), + id + FROM buy b + WHERE id = vBuyFk; END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/procedures/item_comparative.sql b/db/routines/vn/procedures/item_comparative.sql index e72188363..d429cf009 100644 --- a/db/routines/vn/procedures/item_comparative.sql +++ b/db/routines/vn/procedures/item_comparative.sql @@ -6,7 +6,7 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_comparative`( vAvailableSince DATE, vBuyerFk INT, vIsFloramondo BOOL, - vCountryFk INT + vCountryFk INT ) proc: BEGIN /** @@ -23,7 +23,6 @@ proc: BEGIN * @param tmp.comparativeFilterType(filterFk INT ,itemTypeFk INT) * @return tmp.comparative */ - DECLARE vDayRangeStart DATE; DECLARE vDayRangeEnd DATE; DECLARE w1, w2, w3, w4, w5, w6, w7 INT; @@ -59,14 +58,14 @@ proc: BEGIN END IF; SELECT MIN(dated) INTO vDayRangeStart - FROM vn.time + FROM `time` WHERE dated <= vDate GROUP BY period ORDER BY dated desc LIMIT 1 OFFSET vWeekRange; SELECT MAX(dated) INTO vDayRangeEnd - FROM vn.time + FROM `time` WHERE dated >= vDate GROUP BY period ORDER BY dated ASC @@ -83,12 +82,11 @@ proc: BEGIN JOIN itemType t ON t.id = i.typeFk JOIN itemCategory c ON c.id = t.categoryFk LEFT JOIN worker w ON w.id = t.workerFk - WHERE (NOT vHasTypeFilter - OR t.id IN (SELECT itemTypeFk FROM tmp.comparativeFilterType)) - AND (vBuyerFk IS NULL - OR t.workerFk = vBuyerFk) - AND (vIsFloramondo IS NULL - OR i.isFloramondo = vIsFloramondo); + WHERE (NOT vHasTypeFilter OR t.id IN ( + SELECT itemTypeFk FROM tmp.comparativeFilterType + )) + AND (vBuyerFk IS NULL OR t.workerFk = vBuyerFk) + AND (vIsFloramondo IS NULL OR i.isFloramondo = vIsFloramondo); IF vDate < util.VN_CURDATE() THEN ALTER TABLE tmp.itemInventory @@ -115,10 +113,11 @@ proc: BEGIN SET i = i + 1; SELECT t.period INTO vPeriod - FROM vn.`time` t + FROM `time` t WHERE t.dated = vDayRangeStart + INTERVAL (vWeekCount * (i - 1)) DAY; - INSERT IGNORE INTO tTable(cy, ly, zy) VALUES(vPeriod, vPeriod - 100, vPeriod - 200); + INSERT IGNORE INTO tTable(cy, ly, zy) + VALUES(vPeriod, vPeriod - 100, vPeriod - 200); UNTIL i = vWeekCount END REPEAT; SELECT cy, ly, zy INTO w1, y1, z1 FROM tTable LIMIT 1; @@ -130,7 +129,6 @@ proc: BEGIN SELECT cy, ly, zy INTO w7, y7, z7 FROM tTable WHERE cy > w6 LIMIT 1; -- Genera una tabla con los datos del año pasado. - CREATE OR REPLACE TEMPORARY TABLE tLastYear (KEY (lItemFk)) ENGINE = MEMORY @@ -151,15 +149,14 @@ proc: BEGIN SUM(IF(c.timePeriod = y7, c.price, 0)) lprice7 FROM tmp.itemInventory ai JOIN comparative c ON c.itemFk = ai.id - JOIN warehouse w on w.id = c.warehouseFk + JOIN warehouse w ON w.id = c.warehouseFk JOIN tTable wt ON c.timePeriod = wt.ly - WHERE IFNULL(vWarehouseFk, c.warehouseFk) = c.warehouseFk + WHERE (vWarehouseFk IS NULL OR vWarehouseFk = c.warehouseFk) AND w.isComparative AND (vCountryFk IS NULL OR c.countryFk = vCountryFk) GROUP BY ai.id; - -- Genera una tabla con los datos de hace DOS años. - + -- Genera una tabla con los datos de hace 2 años CREATE OR REPLACE TEMPORARY TABLE tTwoYearsAgo (KEY (tItemFk)) ENGINE = MEMORY @@ -180,73 +177,72 @@ proc: BEGIN SUM(IF(c.timePeriod = z7, c.price, 0)) vlprice7 FROM tmp.itemInventory ai JOIN comparative c ON c.itemFk = ai.id - JOIN warehouse w on w.id = c.warehouseFk + JOIN warehouse w ON w.id = c.warehouseFk JOIN tTable wt ON c.timePeriod = wt.zy - WHERE IFNULL(vWarehouseFk, c.warehouseFk) = c.warehouseFk + WHERE (vWarehouseFk IS NULL OR vWarehouseFk = c.warehouseFk) AND w.isComparative AND (vCountryFk IS NULL OR c.countryFk = vCountryFk) GROUP BY ai.id; - -- Genera una tabla con los datos de este año.ss - + -- Genera una tabla con los datos de este año CREATE OR REPLACE TEMPORARY TABLE tCurrentYear (KEY (cItemFk)) ENGINE = MEMORY SELECT t.itemFk cItemFk, - SUM(IF(week = w1, total, 0)) cweek1, - SUM(IF(week = w2, total, 0)) cweek2, - SUM(IF(week = w3, total, 0)) cweek3, - SUM(IF(week = w4, total, 0)) cweek4, - SUM(IF(week = w5, total, 0)) cweek5, - SUM(IF(week = w6, total, 0)) cweek6, - SUM(IF(week = w7, total, 0)) cweek7, - SUM(IF(week = w1, price, 0)) cprice1, - SUM(IF(week = w2, price, 0)) cprice2, - SUM(IF(week = w3, price, 0)) cprice3, - SUM(IF(week = w4, price, 0)) cprice4, - SUM(IF(week = w5, price, 0)) cprice5, - SUM(IF(week = w6, price, 0)) cprice6, - SUM(IF(week = w7, price, 0)) cprice7 + SUM(IF(`week` = w1, total, 0)) cweek1, + SUM(IF(`week` = w2, total, 0)) cweek2, + SUM(IF(`week` = w3, total, 0)) cweek3, + SUM(IF(`week` = w4, total, 0)) cweek4, + SUM(IF(`week` = w5, total, 0)) cweek5, + SUM(IF(`week` = w6, total, 0)) cweek6, + SUM(IF(`week` = w7, total, 0)) cweek7, + SUM(IF(`week` = w1, price, 0)) cprice1, + SUM(IF(`week` = w2, price, 0)) cprice2, + SUM(IF(`week` = w3, price, 0)) cprice3, + SUM(IF(`week` = w4, price, 0)) cprice4, + SUM(IF(`week` = w5, price, 0)) cprice5, + SUM(IF(`week` = w6, price, 0)) cprice6, + SUM(IF(`week` = w7, price, 0)) cprice7 FROM ( SELECT s.itemFk, ti.period `week`, SUM(s.quantity) total, - TRUNCATE(SUM(s.quantity * s.priceFixed),0) price - FROM ticket t + TRUNCATE(SUM(s.quantity * s.priceFixed), 0) price + FROM ticket t FORCE INDEX (Fecha) JOIN sale s ON t.id = s.ticketFk - JOIN tmp.itemInventory it ON it.id = s.itemFk - JOIN time ti ON ti.dated = DATE(t.shipped) + JOIN tmp.itemInventory it ON it.id = s.itemFk + JOIN `time` ti ON ti.dated = DATE(t.shipped) JOIN item i ON i.id = s.itemFk JOIN itemType tp ON tp.id = i.typeFk JOIN itemCategory ic ON ic.id = tp.categoryFk JOIN warehouse w ON w.id = t.warehouseFk - STRAIGHT_JOIN address ad ON ad.id = t.addressFk - JOIN province p ON p.id = ad.provinceFk + JOIN `address` ad ON ad.id = t.addressFk + JOIN province p ON p.id = ad.provinceFk JOIN `client` c ON c.id = ad.clientFk WHERE t.shipped BETWEEN vDayRangeStart AND util.dayEnd(vDayRangeEnd) - AND c.typeFk IN ('Normal','handMaking') - AND w.id = COALESCE(vWarehouseFk, w.id) + AND c.typeFk IN ('normal', 'handMaking') + AND (vWarehouseFk IS NULL OR vWarehouseFk = w.id) + AND (vCountryFk IS NULL OR p.countryFk = vCountryFk) AND w.isComparative - AND (vCountryFk IS NULL OR p.countryFk = vCountryFk) - GROUP BY i.id, week + GROUP BY i.id, `week` ) t GROUP BY t.itemFk; - -- Genera la tabla con la comparativa. + -- Genera la tabla con la comparativa CREATE OR REPLACE TEMPORARY TABLE tmp.comparative ENGINE = MEMORY - SELECT it.subName productor, - b.packing, + SELECT it.subName productor, + b.packing, b.buyingValue costefijo, b.groupingMode caja, it.image ArticleImage, - IFNULL(it.inkFk,"?") color, + IFNULL(it.inkFk, '?') color, tp.code tipo, it.typeFk tipo_id, o.code origen, it.category categoria, it.stems tallos, - it.size medida, + it.`size` medida, it.name article, w.code codigoTrabajador, tp.categoryFk reino_id, @@ -257,24 +253,27 @@ proc: BEGIN it.id Id_Article, i.buy_id, tp.life, - IFNULL(i.sd,0) sd, + IFNULL(i.sd, 0) sd, i.avalaible, i.visible, i.buy_date, e.id provider_id, it.comment comments, it.description itemDescription, - IF(cy.cItemFk IS NULL AND i.visible = 0 AND i.avalaible = 0 - AND IFNULL(i.sd, 0) = 0, FALSE, TRUE) filtret, + IF(cy.cItemFk IS NULL AND i.visible = 0 + AND i.avalaible = 0 AND (i.sd IS NULL OR i.sd = 0), + FALSE, + TRUE + ) filtret, IF(it.hasMinPrice, FORMAT(it.minPrice, 2), "") pvp, s.company_name FROM tmp.itemInventory i JOIN item it ON it.id = i.id - LEFT JOIN itemType tp ON tp.id = it.typeFk - LEFT JOIN worker w ON w.id = tp.workerFk + JOIN itemType tp ON tp.id = it.typeFk + JOIN worker w ON w.id = tp.workerFk LEFT JOIN buy b ON b.id = i.buy_id - LEFT JOIN entry e ON e.id = b.entryFk - LEFT JOIN origin o ON o.id = it.originFk + LEFT JOIN `entry` e ON e.id = b.entryFk + JOIN origin o ON o.id = it.originFk LEFT JOIN tLastYear ly ON ly.lItemFk = it.id LEFT JOIN tCurrentYear cy ON cy.cItemFk = it.id LEFT JOIN tTwoYearsAgo zy ON zy.tItemFk = it.id @@ -287,8 +286,8 @@ proc: BEGIN OR ly.lweek1 OR ly.lweek2 OR ly.lweek3 OR ly.lweek4 OR ly.lweek5 OR ly.lweek6 OR ly.lweek7 OR zy.vlweek1 OR zy.vlweek2 OR zy.vlweek3 OR zy.vlweek4 OR zy.vlweek5 OR zy.vlweek6 OR zy.vlweek7; - -- Elimina las tablas temporales creadas... - DROP TEMPORARY TABLE IF EXISTS tmp.itemInventory, + DROP TEMPORARY TABLE IF EXISTS + tmp.itemInventory, tTwoYearsAgo, tLastYear, tCurrentYear, diff --git a/db/routines/vn/procedures/item_ValuateInventory.sql b/db/routines/vn/procedures/item_valuateInventory.sql similarity index 74% rename from db/routines/vn/procedures/item_ValuateInventory.sql rename to db/routines/vn/procedures/item_valuateInventory.sql index bfd96fa82..18aefdf7b 100644 --- a/db/routines/vn/procedures/item_ValuateInventory.sql +++ b/db/routines/vn/procedures/item_valuateInventory.sql @@ -1,5 +1,7 @@ DELIMITER $$ -CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_ValuateInventory`(vDated DATE, vIsDetailed BOOLEAN) +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_valuateInventory`( + vDated DATE +) BEGIN DECLARE vInventoried DATE; DECLARE vHasNotInventory BOOLEAN DEFAULT FALSE; @@ -15,8 +17,7 @@ BEGIN SELECT tr.landed INTO vInventoried FROM travel tr JOIN `entry` e ON e.travelFk = tr.id - JOIN entryConfig ec - WHERE landed <= vDateDayEnd + WHERE tr.landed <= vDateDayEnd AND e.supplierFk = vInventorySupplierFk ORDER BY tr.landed DESC LIMIT 1; @@ -27,8 +28,7 @@ BEGIN SELECT landed INTO vInventoryClone FROM travel tr JOIN `entry` e ON e.travelFk = tr.id - JOIN entryConfig ec - WHERE landed >= vDated + WHERE tr.landed >= vDated AND e.supplierFk = vInventorySupplierFk ORDER BY landed ASC LIMIT 1; @@ -38,13 +38,14 @@ BEGIN END IF; CREATE OR REPLACE TEMPORARY TABLE tInventory( - warehouseFk SMALLINT, - itemFk BIGINT, - quantity INT, - cost DOUBLE DEFAULT 0, - total DOUBLE DEFAULT 0, - warehouseInventory VARCHAR(20), - PRIMARY KEY (warehouseInventory, itemFk) USING HASH + warehouseFk SMALLINT, + itemFk BIGINT, + quantity INT, + volume DECIMAL(10,2), + cost DOUBLE DEFAULT 0, + total DOUBLE DEFAULT 0, + warehouseInventory VARCHAR(20), + PRIMARY KEY (warehouseInventory, itemFk) USING HASH ) ENGINE = MEMORY; @@ -60,9 +61,8 @@ BEGIN JOIN `entry` e ON e.id = b.entryFk JOIN travel tr ON tr.id = e.travelFk JOIN itemType t ON t.id = i.typeFk - JOIN warehouse w ON w.id = warehouseInFk - JOIN entryConfig ec - WHERE landed = vDateDayEnd + JOIN warehouse w ON w.id = tr.warehouseInFk + WHERE tr.landed = vDateDayEnd AND e.supplierFk = vInventorySupplierFk AND w.valuatedInventory AND t.isInventory @@ -78,9 +78,8 @@ BEGIN JOIN `entry` e ON e.id = b.entryFk JOIN travel tr ON tr.id = e.travelFk JOIN itemType t ON t.id = i.typeFk - JOIN warehouse w ON w.id = warehouseInFk - JOIN entryConfig ec - WHERE landed = vInventoried + JOIN warehouse w ON w.id = tr.warehouseInFk + WHERE tr.landed = vInventoried AND e.supplierFk = vInventorySupplierFk AND w.valuatedInventory AND t.isInventory @@ -99,7 +98,6 @@ BEGIN JOIN travel tr ON tr.id = e.travelFk JOIN itemType t ON t.id = i.typeFk JOIN warehouse w ON w.id = tr.warehouseInFk - JOIN entryConfig ec WHERE tr.landed BETWEEN vInventoried AND vDateDayEnd AND IF(tr.landed = util.VN_CURDATE(), tr.isReceived, TRUE) AND NOT e.isRaid @@ -183,52 +181,37 @@ BEGIN AND e.isConfirmed ON DUPLICATE KEY UPDATE tInventory.quantity = tInventory.quantity + (b.quantity); - CALL vn.buyUltimate(NULL, vDateDayEnd); + CALL buyUltimate(NULL, vDateDayEnd); + + DELETE FROM tInventory WHERE quantity IS NULL OR NOT quantity; UPDATE tInventory i JOIN tmp.buyUltimate bu ON i.warehouseFk = bu.warehouseFk AND i.itemFk = bu.itemFk JOIN buy b ON b.id = bu.buyFk - SET total = i.quantity * (IFNULL(b.buyingValue, 0) + IFNULL(b.packageValue, 0) + IFNULL(b.freightValue, 0) + IFNULL(b.comissionValue, 0)), - cost = IFNULL(b.buyingValue, 0) + IFNULL(b.packageValue, 0) + IFNULL(b.freightValue, 0) + IFNULL(b.comissionValue, 0) - WHERE i.quantity; + LEFT JOIN itemCost ic ON ic.itemFk = i.itemFk + AND ic.warehouseFk = i.warehouseFk + SET i.total = i.quantity * (IFNULL(b.buyingValue, 0) + IFNULL(b.packageValue, 0) + IFNULL(b.freightValue, 0) + IFNULL(b.comissionValue, 0)), + i.cost = IFNULL(b.buyingValue, 0) + IFNULL(b.packageValue, 0) + IFNULL(b.freightValue, 0) + IFNULL(b.comissionValue, 0), + i.volume = i.quantity * ic.cm3delivery / 1000000; - DELETE FROM tInventory - WHERE quantity IS NULL OR NOT quantity; - - IF vIsDetailed THEN - SELECT ti.warehouseFk, - i.id itemFk, - i.longName, - i.size, - ti.quantity, - tp.name Tipo, - ic.name Reino, - ti.cost, - CAST(ti.total AS DECIMAL(10, 2)) total, - ti.warehouseInventory almacen - FROM tInventory ti - JOIN warehouse w ON w.id = warehouseFk - JOIN item i ON i.id = ti.itemFk - JOIN itemType tp ON tp.id = i.typeFk - JOIN itemCategory ic ON ic.id = tp.categoryFk - WHERE w.valuatedInventory - AND ti.total > 0 - ORDER BY ti.total DESC; - ELSE - SELECT i.warehouseInventory Almacen, - ic.name Reino, - CAST(i.total AS DECIMAL(10, 2)) Euros, - w.code Comprador, - it.id - FROM tInventory i - JOIN warehouse wh ON wh.id = warehouseFk - JOIN item it ON it.id = i.itemFk - JOIN itemType itp ON itp.id = it.typeFk - LEFT JOIN worker w ON w.id = itp.workerFk - JOIN itemCategory ic ON ic.id = itp.categoryFk - WHERE wh.valuatedInventory - AND i.total > 0; - END IF; + SELECT ti.warehouseFk, + i.id, + i.longName, + i.size, + ti.quantity, + ti.volume, + tp.name itemTypeName, + ic.name itemCategoryName, + ti.cost, + ti.total, + ti.warehouseInventory + FROM tInventory ti + JOIN warehouse w ON w.id = warehouseFk + JOIN item i ON i.id = ti.itemFk + JOIN itemType tp ON tp.id = i.typeFk + JOIN itemCategory ic ON ic.id = tp.categoryFk + WHERE w.valuatedInventory + AND ti.total > 0; DROP TEMPORARY TABLE tmp.buyUltimate, diff --git a/db/routines/vn/procedures/ticketPackaging_add.sql b/db/routines/vn/procedures/ticketPackaging_add.sql index d669b95f5..f96068b56 100644 --- a/db/routines/vn/procedures/ticketPackaging_add.sql +++ b/db/routines/vn/procedures/ticketPackaging_add.sql @@ -27,7 +27,10 @@ BEGIN SELECT DISTINCT clientFk FROM ( SELECT clientFk, SUM(quantity) totalQuantity - FROM tmp.packagingToInvoice + FROM tmp.packagingToInvoice tpi + JOIN client c ON c.id = tpi.clientFk + LEFT JOIN supplier s ON s.nif = c.fi + WHERE s.id IS NULL GROUP BY itemFk, clientFk HAVING totalQuantity > 0)sub; diff --git a/db/routines/vn/procedures/travel_throwAwb.sql b/db/routines/vn/procedures/travel_throwAwb.sql new file mode 100644 index 000000000..1b54f8532 --- /dev/null +++ b/db/routines/vn/procedures/travel_throwAwb.sql @@ -0,0 +1,14 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`travel_throwAwb`(vSelf INT) +BEGIN +/** + * Throws an error if travel does not have a logical AWB + * or there are several AWBs associated with the same DUA + * + * @param vSelf The travel id + */ + IF NOT travel_hasUniqueAwb(vSelf) THEN + CALL util.throw('A different AWB is found in the entries'); + END IF; +END$$ +DELIMITER ; \ No newline at end of file diff --git a/db/routines/vn/triggers/entry_beforeInsert.sql b/db/routines/vn/triggers/entry_beforeInsert.sql index c0c0aa28c..17831f1b8 100644 --- a/db/routines/vn/triggers/entry_beforeInsert.sql +++ b/db/routines/vn/triggers/entry_beforeInsert.sql @@ -7,8 +7,8 @@ BEGIN CALL supplier_checkIsActive(NEW.supplierFk); SET NEW.currencyFk = entry_getCurrency(NEW.currencyFk, NEW.supplierFk); SET NEW.commission = entry_getCommission(NEW.travelFk, NEW.currencyFk,NEW.supplierFk); - IF NEW.travelFk IS NOT NULL AND NOT travel_hasUniqueAwb(NEW.travelFk) THEN - CALL util.throw('The travel is incorrect, there is a different AWB in the associated entries'); + IF NEW.travelFk IS NOT NULL THEN + CALL travel_throwAwb(NEW.travelFk); END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/entry_beforeUpdate.sql b/db/routines/vn/triggers/entry_beforeUpdate.sql index d56db5e01..e99eb087d 100644 --- a/db/routines/vn/triggers/entry_beforeUpdate.sql +++ b/db/routines/vn/triggers/entry_beforeUpdate.sql @@ -25,8 +25,8 @@ BEGIN IF NOT (NEW.travelFk <=> OLD.travelFk) THEN - IF NEW.travelFk IS NOT NULL AND NOT travel_hasUniqueAwb(NEW.travelFk) THEN - CALL util.throw('The travel is incorrect, there is a different AWB in the associated entries'); + IF NEW.travelFk IS NOT NULL THEN + CALL travel_throwAwb(NEW.travelFk); END IF; SELECT COUNT(*) > 0 INTO vIsVirtual diff --git a/db/routines/vn/triggers/travel_beforeInsert.sql b/db/routines/vn/triggers/travel_beforeInsert.sql index 817bd69bb..e54a5d08b 100644 --- a/db/routines/vn/triggers/travel_beforeInsert.sql +++ b/db/routines/vn/triggers/travel_beforeInsert.sql @@ -9,8 +9,8 @@ BEGIN CALL travel_checkWarehouseIsFeedStock(NEW.warehouseInFk); - IF NEW.awbFk IS NOT NULL AND NOT travel_hasUniqueAwb(NEW.id) THEN - CALL util.throw('The AWB is incorrect, there is a different AWB in the associated entries'); + IF NEW.awbFk IS NOT NULL THEN + CALL travel_throwAwb(NEW.id); END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/travel_beforeUpdate.sql b/db/routines/vn/triggers/travel_beforeUpdate.sql index 5e43c8761..5e64ad5b3 100644 --- a/db/routines/vn/triggers/travel_beforeUpdate.sql +++ b/db/routines/vn/triggers/travel_beforeUpdate.sql @@ -33,8 +33,8 @@ BEGIN END IF; END IF; - IF (NOT(NEW.awbFk <=> OLD.awbFk)) AND NEW.awbFk IS NOT NULL AND NOT travel_hasUniqueAwb(NEW.id) THEN - CALL util.throw('The AWB is incorrect, there is a different AWB in the associated entries'); + IF (NOT(NEW.awbFk <=> OLD.awbFk)) AND NEW.awbFk IS NOT NULL THEN + CALL travel_throwAwb(NEW.id); END IF; END$$ DELIMITER ; diff --git a/db/versions/10978-wheatMoss/00-firstScript.sql b/db/versions/10978-wheatMoss/00-firstScript.sql new file mode 100644 index 000000000..39bf1c318 --- /dev/null +++ b/db/versions/10978-wheatMoss/00-firstScript.sql @@ -0,0 +1,4 @@ +-- Place your SQL code here +INSERT INTO salix.defaultViewConfig +(tableCode, `columns`) +VALUES('routesList', '{"ID":true,"worker":true,"agency":true,"vehicle":true,"date":true,"volume":true,"description":true,"started":true,"finished":true,"actions":true}'); diff --git a/db/versions/11015-silverBamboo/00-photoMotivation.sql b/db/versions/11015-silverBamboo/00-photoMotivation.sql new file mode 100644 index 000000000..366694e12 --- /dev/null +++ b/db/versions/11015-silverBamboo/00-photoMotivation.sql @@ -0,0 +1 @@ +ALTER TABLE vn.item ADD COLUMN photoMotivation VARCHAR(255); \ No newline at end of file diff --git a/db/versions/11050-wheatAnthurium/00-firstScript.sql b/db/versions/11050-wheatAnthurium/00-firstScript.sql new file mode 100644 index 000000000..cb8034ff5 --- /dev/null +++ b/db/versions/11050-wheatAnthurium/00-firstScript.sql @@ -0,0 +1,4 @@ +-- Place your SQL code here + +ALTER TABLE floranet.catalogue ADD addressFk int(11) NOT NULL; +ALTER TABLE floranet.catalogue ADD CONSTRAINT catalogue_address_FK FOREIGN KEY (addressFk) REFERENCES vn.address(id) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/db/versions/11050-wheatAnthurium/01-elementFilter.sql b/db/versions/11050-wheatAnthurium/01-elementFilter.sql new file mode 100644 index 000000000..3f2bb78ac --- /dev/null +++ b/db/versions/11050-wheatAnthurium/01-elementFilter.sql @@ -0,0 +1,6 @@ +ALTER TABLE floranet.builder DROP FOREIGN KEY builder_FK_1; +ALTER TABLE floranet.`element` DROP PRIMARY KEY; +ALTER TABLE floranet.`element` ADD id INT NOT NULL; +ALTER TABLE floranet.`element` ADD CONSTRAINT element_pk PRIMARY KEY (id); +ALTER TABLE floranet.builder ADD CONSTRAINT builder_element_FK FOREIGN KEY (elementFk) REFERENCES floranet.`element`(id) ON DELETE CASCADE ON UPDATE CASCADE; + diff --git a/db/versions/11059-crimsonAnthurium/00-firstScript.sql b/db/versions/11059-crimsonAnthurium/00-firstScript.sql new file mode 100644 index 000000000..b0eade302 --- /dev/null +++ b/db/versions/11059-crimsonAnthurium/00-firstScript.sql @@ -0,0 +1,3 @@ +-- Place your SQL code here +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('RouteConfig','*','READ','ALLOW','ROLE','employee'); diff --git a/db/versions/11060-tealGalax/00-createRoleReviewer.sql b/db/versions/11060-tealGalax/00-createRoleReviewer.sql new file mode 100644 index 000000000..bf2984702 --- /dev/null +++ b/db/versions/11060-tealGalax/00-createRoleReviewer.sql @@ -0,0 +1,46 @@ +use account; + +INSERT INTO role + SET name = 'reviewer', + description = 'Revisor de producción', + hasLogin = TRUE, + created = util.VN_CURDATE(), + modified = util.VN_CURDATE(), + editorFk = NULL; + +INSERT INTO roleInherit( + role, + inheritsFrom +) + SELECT r1.id, + r2.id + FROM role r1 + JOIN role r2 + WHERE r1.name = 'reviewer' + AND r2.name = 'production' + UNION + SELECT ri.role, + r2.id + FROM roleInherit ri + JOIN role r1 ON r1.id = ri.role + JOIN role r2 ON r2.name = 'reviewer' + WHERE r1.name IN ('claimManager', 'productionBoss') + GROUP BY ri.role; + +DELETE ri + FROM roleInherit ri + JOIN role r1 ON ri.role = r1.id + JOIN role r2 ON ri.inheritsFrom = r2.id + WHERE r1.name = 'replenisher' + AND r2.name = 'buyer'; + +UPDATE salix.ACL + SET principalId = 'reviewer' + WHERE property = 'isInPreparing'; + +UPDATE user u + JOIN vn.workerDepartment wd ON wd.workerFk = u.id + JOIN vn.department d ON wd.departmentFk = d.id + JOIN role r ON r.name = 'reviewer' + SET u.role = r.id + WHERE d.name IN ('REVISION', 'PREVIA'); \ No newline at end of file diff --git a/db/versions/11065-yellowChrysanthemum/00-modifyInvoiceInPrivileges.sql b/db/versions/11065-yellowChrysanthemum/00-modifyInvoiceInPrivileges.sql new file mode 100644 index 000000000..b65ca1c17 --- /dev/null +++ b/db/versions/11065-yellowChrysanthemum/00-modifyInvoiceInPrivileges.sql @@ -0,0 +1,24 @@ +REVOKE UPDATE ON vn. invoiceIn FROM administrative, hrBoss, buyer, logistic; +GRANT UPDATE (id, + serialNumber, + serial, + supplierFk, + issued, + supplierRef, + currencyFk, + created, + companyFk, + docFk, + booked, + operated, + siiTypeInvoiceInFk, + cplusRectificationTypeFk, + cplusSubjectOpFk, + cplusTaxBreakFk, + siiTrascendencyInvoiceInFk, + bookEntried, + isVatDeductible, + withholdingSageFk, + expenseFkDeductible, + editorFk +) ON vn.invoiceIn TO administrative, hrBoss, buyer, logistic; \ No newline at end of file diff --git a/db/versions/11065-yellowChrysanthemum/01-modifyInvoiceInAcls.sql b/db/versions/11065-yellowChrysanthemum/01-modifyInvoiceInAcls.sql new file mode 100644 index 000000000..5475b70ac --- /dev/null +++ b/db/versions/11065-yellowChrysanthemum/01-modifyInvoiceInAcls.sql @@ -0,0 +1,23 @@ +UPDATE salix.ACL + SET accessType = 'READ' + WHERE principalId IN ('administrative','buyer') + AND model = 'invoiceIn' + AND property = '*'; + +INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) +VALUES + ('InvoiceIn', 'updateInvoiceIn', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'clone', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'corrective', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'exchangeRateUpdate', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'invoiceInEmail', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'toBook', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'toUnbook', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'deleteById', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', 'updateInvoiceIn', 'WRITE', 'ALLOW', 'ROLE', 'buyer'), + ('InvoiceIn', 'clone', 'WRITE', 'ALLOW', 'ROLE', 'buyer'), + ('InvoiceIn', 'corrective', 'WRITE', 'ALLOW', 'ROLE', 'buyer'), + ('InvoiceIn', 'exchangeRateUpdate', 'WRITE', 'ALLOW', 'ROLE', 'buyer'), + ('InvoiceIn', 'invoiceInEmail', 'WRITE', 'ALLOW', 'ROLE', 'buyer'), + ('InvoiceIn', 'toBook', 'WRITE', 'ALLOW', 'ROLE', 'buyer'), + ('InvoiceIn', 'deleteById', 'WRITE', 'ALLOW', 'ROLE', 'buyer'); diff --git a/db/versions/11068-blueGerbera/00-firstScript.sql b/db/versions/11068-blueGerbera/00-firstScript.sql new file mode 100644 index 000000000..6342981a7 --- /dev/null +++ b/db/versions/11068-blueGerbera/00-firstScript.sql @@ -0,0 +1,9 @@ +-- Place your SQL code here +CREATE OR REPLACE TABLE floranet.config ( + email varchar(255) DEFAULT 'floranet@verdnatura.es' NOT NULL +) +ENGINE=InnoDB +DEFAULT CHARSET=utf8mb3 +COLLATE=utf8mb3_unicode_ci; + + diff --git a/db/versions/11069-brownChrysanthemum/00-firstScript.sql b/db/versions/11069-brownChrysanthemum/00-firstScript.sql new file mode 100644 index 000000000..3220d1257 --- /dev/null +++ b/db/versions/11069-brownChrysanthemum/00-firstScript.sql @@ -0,0 +1,35 @@ +-- Place your SQL code here +DROP TABLE IF EXISTS floranet.builder; + +CREATE OR REPLACE TABLE floranet.`element` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL, + `itemFk` int(11) DEFAULT NULL, + `longNameFilter` varchar(30) DEFAULT NULL, + `typeFk` smallint(5) unsigned DEFAULT NULL, + `minSize` int(10) unsigned DEFAULT NULL, + `maxSize` int(10) unsigned DEFAULT NULL, + `inkFk` char(3) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `originFk` tinyint(2) unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `element_FK` (`itemFk`), + KEY `element_FK_1` (`typeFk`), + KEY `element_FK_2` (`inkFk`), + KEY `element_FK_3` (`originFk`), + CONSTRAINT `element_FK` FOREIGN KEY (`itemFk`) REFERENCES `vn`.`item` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `element_FK_1` FOREIGN KEY (`typeFk`) REFERENCES `vn`.`itemType` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `element_FK_2` FOREIGN KEY (`inkFk`) REFERENCES `vn`.`ink` (`id`) ON UPDATE CASCADE, + CONSTRAINT `element_FK_3` FOREIGN KEY (`originFk`) REFERENCES `vn`.`origin` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Filtro para localizar posibles items que coincidan con la descripción'; + +CREATE OR REPLACE TABLE floranet.`recipe` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `itemFk` int(11) NOT NULL, + `elementFk` int(11) NOT NULL, + `quantity` int(10) unsigned NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + KEY `recipe_FK` (`itemFk`), + KEY `recipe_FK_1` (`elementFk`), + CONSTRAINT `recipe_FK` FOREIGN KEY (`itemFk`) REFERENCES `vn`.`item` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `recipe_element_FK` FOREIGN KEY (`elementFk`) REFERENCES `element` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Links handmade products with their elements'; diff --git a/db/versions/11074-brownRuscus/00-firstScript.sql b/db/versions/11074-brownRuscus/00-firstScript.sql new file mode 100644 index 000000000..f78ba98ae --- /dev/null +++ b/db/versions/11074-brownRuscus/00-firstScript.sql @@ -0,0 +1,20 @@ +-- Place your SQL code here + + +-- floranet.recipe definition + +CREATE OR REPLACE TABLE floranet.`recipe` + ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `itemFk` int(11) NOT NULL COMMENT 'Bouquet or plant name', + `elementFk` int(11) NOT NULL COMMENT 'Item detail for bouquet''s composition', + `quantity` int(10) unsigned NOT NULL DEFAULT 1, + `cost` decimal(10,2) NOT NULL DEFAULT 1.00, + PRIMARY KEY (`id`), + KEY `recipe_FK` (`itemFk`), + KEY `recipe_FK_1` (`elementFk`), + CONSTRAINT `recipe_FK` FOREIGN KEY (`itemFk`) REFERENCES `vn`.`item` (`id`) ON UPDATE CASCADE, + CONSTRAINT `recipe_item_FK` FOREIGN KEY (`elementFk`) REFERENCES `vn`.`item` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Links handmade products with their elements'; + +DROP TABLE IF EXISTS floranet.`element`; \ No newline at end of file diff --git a/db/versions/11078-goldenFern/00-firstScript.sql b/db/versions/11078-goldenFern/00-firstScript.sql new file mode 100644 index 000000000..63888a1bb --- /dev/null +++ b/db/versions/11078-goldenFern/00-firstScript.sql @@ -0,0 +1,2 @@ +ALTER TABLE vn.productionConfig ADD scannablePreviusCodeType enum('qr','barcode') + CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT 'barcode' NOT NULL; \ No newline at end of file diff --git a/e2e/paths/02-client/05_add_address.spec.js b/e2e/paths/02-client/05_add_address.spec.js index 0581635d0..4f809a716 100644 --- a/e2e/paths/02-client/05_add_address.spec.js +++ b/e2e/paths/02-client/05_add_address.spec.js @@ -51,7 +51,7 @@ describe('Client Add address path', () => { await page.waitToClick(selectors.clientAddresses.saveButton); const message = await page.waitForSnackbar(); - expect(message.text).toContain('Incoterms is required for a non UEE member'); + expect(message.text).toContain('Incoterms and Customs agent are required for a non UEE member'); }); it(`should receive an error after clicking save button as customsAgent is empty`, async() => { @@ -59,7 +59,7 @@ describe('Client Add address path', () => { await page.waitToClick(selectors.clientAddresses.saveButton); const message = await page.waitForSnackbar(); - expect(message.text).toContain('Customs agent is required for a non UEE member'); + expect(message.text).toContain('Incoterms and Customs agent are required for a non UEE member'); }); it(`should create a new custom agent and then save the address`, async() => { diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js index 4f54ad860..e0f32fc3a 100644 --- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js +++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js @@ -225,7 +225,7 @@ describe('Ticket Edit sale path', () => { }); it('should show error trying to delete a ticket with a refund', async() => { - await page.loginAndModule('production', 'ticket'); + await page.loginAndModule('salesPerson', 'ticket'); await page.accessToSearchResult('8'); await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket); diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml index 17e955ff5..7fcb8c16b 100644 --- a/front/core/locale/es.yml +++ b/front/core/locale/es.yml @@ -69,3 +69,4 @@ Send cau: Enviar cau By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc ExplainReason: Explique el motivo por el que no deberia aparecer este fallo You already have the mailAlias: Ya tienes este alias de correo +Error loading ACLs: Error al cargar los ACLs diff --git a/front/core/services/auth.js b/front/core/services/auth.js index 753bc3fba..0cae4bae8 100644 --- a/front/core/services/auth.js +++ b/front/core/services/auth.js @@ -7,16 +7,17 @@ import UserError from 'core/lib/user-error'; * @property {Boolean} loggedIn Whether the user is currently logged */ export default class Auth { - constructor($http, $q, $state, $transitions, $window, vnToken, vnModules, aclService) { + constructor($http, $q, vnApp, $translate, $state, $transitions, $window, vnToken, vnModules) { Object.assign(this, { $http, $q, + vnApp, + $translate, $state, $transitions, $window, vnToken, vnModules, - aclService, loggedIn: false }); } @@ -39,9 +40,26 @@ export default class Auth { }; if (this.vnToken.token) { - return this.loadAcls() - .then(() => true) - .catch(redirectToLogin); + const loadWithRetry = () => { + return this.validateToken() + .then(() => true) + .catch(err => { + switch (err.status) { + case 400: + case 401: + return redirectToLogin(); + default: + return new Promise(resolve => { + setTimeout(() => { + this.vnApp.showMessage(this.$translate.instant('Loading...')); + + resolve(loadWithRetry()); + }, 2000); + }); + } + }); + }; + return loadWithRetry(); } else return redirectToLogin(); }); @@ -87,13 +105,11 @@ export default class Auth { headers: {Authorization: json.data.token} }).then(({data}) => { this.vnToken.set(json.data.token, data.multimediaToken.id, now, json.data.ttl, remember); - this.loadAcls().then(() => { - let continueHash = this.$state.params.continue; - if (continueHash) - this.$window.location = continueHash; - else - this.$state.go('home'); - }); + let continueHash = this.$state.params.continue; + if (continueHash) + this.$window.location = continueHash; + else + this.$state.go('home'); }).catch(() => {}); } @@ -107,24 +123,25 @@ export default class Auth { this.vnToken.unset(); this.loggedIn = false; this.vnModules.reset(); - this.aclService.reset(); + this.vnModules.aclService.reset(); this.$state.go('login'); return promise; } - loadAcls() { - return this.aclService.load() + validateToken() { + return this.$http.get('VnUsers/validateToken') .then(() => { this.loggedIn = true; this.vnModules.reset(); }) .catch(err => { - this.vnToken.unset(); throw err; }); } } -Auth.$inject = ['$http', '$q', '$state', '$transitions', '$window', 'vnToken', 'vnModules', 'aclService']; +Auth.$inject = [ + '$http', '$q', 'vnApp', '$translate', '$state', + '$transitions', '$window', 'vnToken', 'vnModules']; ngModule.service('vnAuth', Auth); diff --git a/front/salix/components/upload-photo/index.js b/front/salix/components/upload-photo/index.js index c9774d037..7779c81e1 100644 --- a/front/salix/components/upload-photo/index.js +++ b/front/salix/components/upload-photo/index.js @@ -164,6 +164,7 @@ export default class UploadPhoto extends Component { const options = { type: 'blob', + size: 'original' }; return this.editor.result(options) .then(blob => this.newPhoto.blob = blob) diff --git a/front/salix/routes.js b/front/salix/routes.js index 8621f83c7..be210b749 100644 --- a/front/salix/routes.js +++ b/front/salix/routes.js @@ -12,7 +12,8 @@ function config($stateProvider, $urlRouterProvider) { template: '', resolve: { config: ['vnConfig', vnConfig => vnConfig.initialize()], - token: ['vnToken', vnToken => vnToken.fetchConfig()] + token: ['vnToken', vnToken => vnToken.fetchConfig()], + acl: ['aclService', aclService => aclService.load()] } }) .state('outLayout', { diff --git a/loopback/common/models/application.js b/loopback/common/models/application.js index 6bdc2c13a..80c58ddc1 100644 --- a/loopback/common/models/application.js +++ b/loopback/common/models/application.js @@ -1,4 +1,3 @@ - module.exports = function(Self) { require('../methods/application/status')(Self); require('../methods/application/post')(Self); diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 551b544b6..cb9e1d12c 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -61,7 +61,8 @@ "Changed sale discount": "I have changed the following lines discounts from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Created claim": "I have created the claim [{{claimId}}]({{{claimUrl}}}) for the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Changed sale price": "I have changed the price of [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) from {{oldPrice}}€ ➔ *{{newPrice}}€* of the ticket [{{ticketId}}]({{{ticketUrl}}})", - "Changed sale quantity": "I have changed the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "I have changed {{changes}} of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changes in sales": "the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}*", "Changed sale reserved state": "I have changed the following lines reserved state from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", "Bought units from buy request": "Bought {{quantity}} units of [{{itemId}} {{concept}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*", @@ -227,5 +228,6 @@ "They're not your subordinate": "They're not your subordinate", "InvoiceIn is already booked": "InvoiceIn is already booked", "This workCenter is already assigned to this agency": "This workCenter is already assigned to this agency", - "You can only have one PDA": "You can only have one PDA" -} \ No newline at end of file + "You can only have one PDA": "You can only have one PDA", + "Incoterms and Customs agent are required for a non UEE member": "Incoterms and Customs agent are required for a non UEE member" +} diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 4a7e1505c..f94e21d7b 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -124,7 +124,8 @@ "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}}}", "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 {{changes}} del ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changes in sales": "la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}*", "State": "Estado", "regular": "normal", "reserved": "reservado", @@ -358,6 +359,8 @@ "Select ticket or client": "Elija un ticket o un client", "It was not able to create the invoice": "No se pudo crear la factura", "ticketCommercial": "El ticket {{ ticket }} para el vendedor {{ salesMan }} está en preparación. (mensaje generado automáticamente)", - "This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario", - "You can only have one PDA": "Solo puedes tener un PDA" -} \ No newline at end of file + "This PDA is already assigned to another user": "Esta PDA ya está asignado a otro usuario", + "You can only have one PDA": "Solo puedes tener una PDA", + "Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE", + "You can not use the same password": "No puedes usar la misma contraseña" +} diff --git a/loopback/locale/fr.json b/loopback/locale/fr.json index 44f5e35d3..6f3919e18 100644 --- a/loopback/locale/fr.json +++ b/loopback/locale/fr.json @@ -123,8 +123,9 @@ "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": "J'ai changé le prix de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* du ticket [{{ticketId}}]({{{ticketUrl}}})", - "Changed sale quantity": "J'ai changé la quantité de {{itemId}} {{concept}} de {{oldQuantity}} ➔ {{newQuantity}} 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", "reserved": "réservé", @@ -357,4 +358,4 @@ "This workCenter is already assigned to this agency": "Ce centre de travail est déjà assigné à cette agence", "Select ticket or client": "Choisissez un ticket ou un client", "It was not able to create the invoice": "Il n'a pas été possible de créer la facture" -} \ No newline at end of file +} diff --git a/loopback/locale/pt.json b/loopback/locale/pt.json index b11eeefc6..3c156506c 100644 --- a/loopback/locale/pt.json +++ b/loopback/locale/pt.json @@ -124,7 +124,8 @@ "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 [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* 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", "reserved": "reservado", @@ -357,4 +358,4 @@ "This workCenter is already assigned to this agency": "Este centro de trabalho já está atribuído a esta agência", "Select ticket or client": "Selecione um ticket ou cliente", "It was not able to create the invoice": "Não foi possível criar a fatura" -} \ No newline at end of file +} diff --git a/loopback/server/boot/isProduction.js b/loopback/server/boot/isProduction.js new file mode 100644 index 000000000..151afa9cb --- /dev/null +++ b/loopback/server/boot/isProduction.js @@ -0,0 +1,3 @@ +module.exports = (localAsProduction = true) => { + return (!process.env.NODE_ENV && localAsProduction) || process.env.NODE_ENV == 'production'; +}; diff --git a/modules/account/back/models/ldap-config.js b/modules/account/back/models/ldap-config.js index 89f0add48..583ce084b 100644 --- a/modules/account/back/models/ldap-config.js +++ b/modules/account/back/models/ldap-config.js @@ -3,9 +3,10 @@ const app = require('vn-loopback/server/server'); const ldap = require('../util/ldapjs-extra'); const crypto = require('crypto'); const nthash = require('smbhash').nthash; +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { - const shouldSync = process.env.NODE_ENV !== 'test'; + const shouldSync = isProduction(); Self.getLinker = async function() { return await Self.findOne({ diff --git a/modules/account/back/models/samba-config.js b/modules/account/back/models/samba-config.js index 927510a29..359b4b187 100644 --- a/modules/account/back/models/samba-config.js +++ b/modules/account/back/models/samba-config.js @@ -1,6 +1,7 @@ const ldap = require('../util/ldapjs-extra'); const execFile = require('child_process').execFile; +const isProduction = require('vn-loopback/server/boot/isProduction'); /** * Summary of userAccountControl flags: @@ -12,7 +13,7 @@ const UserAccountControlFlags = { }; module.exports = Self => { - const shouldSync = process.env.NODE_ENV !== 'test'; + const shouldSync = isProduction(); Self.getLinker = async function() { return await Self.findOne({ diff --git a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js index b05b2ac15..156caaeec 100644 --- a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js +++ b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js @@ -6,6 +6,7 @@ describe('claimBeginning', () => { const claimManagerId = 72; const activeCtx = { accessToken: {userId: claimManagerId}, + __: value => value }; const ctx = {req: activeCtx}; diff --git a/modules/client/back/methods/client/createAddress.js b/modules/client/back/methods/client/createAddress.js index 8e6db2a22..2709632cb 100644 --- a/modules/client/back/methods/client/createAddress.js +++ b/modules/client/back/methods/client/createAddress.js @@ -92,11 +92,8 @@ module.exports = function(Self) { }, myOptions); const isUeeMember = province.country().isUeeMember; - if (!isUeeMember && !args.incotermsFk) - throw new UserError(`Incoterms is required for a non UEE member`); - - if (!isUeeMember && !args.customsAgentFk) - throw new UserError(`Customs agent is required for a non UEE member`); + if (!isUeeMember && (!args.incotermsFk || !args.customsAgentFk)) + throw new UserError(`Incoterms and Customs agent are required for a non UEE member`); delete args.ctx; // Remove unwanted properties const newAddress = await models.Address.create(args, myOptions); diff --git a/modules/client/back/methods/client/specs/createAddress.spec.js b/modules/client/back/methods/client/specs/createAddress.spec.js index 0841ad98c..ae179cf6c 100644 --- a/modules/client/back/methods/client/specs/createAddress.spec.js +++ b/modules/client/back/methods/client/specs/createAddress.spec.js @@ -50,7 +50,7 @@ describe('Address createAddress', () => { } expect(error).toBeDefined(); - expect(error.message).toEqual('Incoterms is required for a non UEE member'); + expect(error.message).toEqual('Incoterms and Customs agent are required for a non UEE member'); }); it('should throw a non uee member error if no customsAgent is defined', async() => { @@ -81,7 +81,7 @@ describe('Address createAddress', () => { } expect(error).toBeDefined(); - expect(error.message).toEqual('Customs agent is required for a non UEE member'); + expect(error.message).toEqual('Incoterms and Customs agent are required for a non UEE member'); }); it('should create a new address and set as a client default address', async() => { diff --git a/modules/client/back/methods/sms/send.js b/modules/client/back/methods/sms/send.js index 94b2b6c27..2b5674f86 100644 --- a/modules/client/back/methods/sms/send.js +++ b/modules/client/back/methods/sms/send.js @@ -1,5 +1,6 @@ const got = require('got'); const UserError = require('vn-loopback/util/user-error'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethod('send', { @@ -47,7 +48,7 @@ module.exports = Self => { let response; try { - if (process.env.NODE_ENV !== 'production') + if (!isProduction(false)) response = {result: [{status: 'ok'}]}; else { const jsonTest = { diff --git a/modules/client/back/models/XDiario.json b/modules/client/back/models/XDiario.json index be543393d..5c277783a 100644 --- a/modules/client/back/models/XDiario.json +++ b/modules/client/back/models/XDiario.json @@ -76,7 +76,16 @@ }, "enlazadoSage": { "type": "boolean" - } + }, + "enlazado": { + "type": "boolean" + }, + "key": { + "type": "number", + "mysql": { + "columnName": "CLAVE" + } + } }, "relations": { "company": { diff --git a/modules/entry/back/model-config.json b/modules/entry/back/model-config.json index 7b6e23685..dc7fd86be 100644 --- a/modules/entry/back/model-config.json +++ b/modules/entry/back/model-config.json @@ -22,5 +22,8 @@ }, "EntryObservation": { "dataSource": "vn" + }, + "EntryType": { + "dataSource": "vn" } -} +} \ No newline at end of file diff --git a/modules/entry/back/models/entry-type.json b/modules/entry/back/models/entry-type.json new file mode 100644 index 000000000..989aa3a8a --- /dev/null +++ b/modules/entry/back/models/entry-type.json @@ -0,0 +1,25 @@ +{ + "name": "EntryType", + "base": "VnModel", + "mixins": { + "Loggable": true + }, + "options": { + "mysql": { + "table": "entryType" + } + }, + "properties": { + "code": { + "type": "string", + "id": true, + "description": "Identifier" + }, + "description": { + "type": "string" + }, + "isInformal": { + "type": "boolean" + } + } +} \ No newline at end of file diff --git a/modules/entry/back/models/entry.json b/modules/entry/back/models/entry.json index ab451219e..833edf14d 100644 --- a/modules/entry/back/models/entry.json +++ b/modules/entry/back/models/entry.json @@ -35,9 +35,9 @@ }, "isVirtual": { "type": "boolean", - "mysql": { - "columnName": "isRaid" - } + "mysql": { + "columnName": "isRaid" + } }, "isRaid": { "type": "boolean" @@ -53,9 +53,9 @@ }, "observation": { "type": "string", - "mysql": { - "columnName": "evaNotes" - } + "mysql": { + "columnName": "evaNotes" + } }, "loadPriority": { "type": "number" @@ -101,6 +101,11 @@ "type": "belongsTo", "model": "Account", "foreignKey": "observationEditorFk" + }, + "entryType": { + "type": "belongsTo", + "model": "EntryType", + "foreignKey": "typeFk" } } -} +} \ No newline at end of file diff --git a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js index 3ad06b242..989b1d4a2 100644 --- a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js +++ b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js @@ -54,6 +54,20 @@ module.exports = Self => { value: rate }); } + const monday = 1; + if (xmlDateWithoutTime.getDay() === monday) { + const saturday = new Date(xmlDateWithoutTime); + saturday.setDate(xmlDateWithoutTime.getDate() - 2); + const sunday = new Date(xmlDateWithoutTime); + sunday.setDate(xmlDateWithoutTime.getDate() - 1); + + for (const date of [saturday, sunday]) { + await models.ReferenceRate.upsertWithWhere( + {currencyFk: currency.id, dated: date}, + {currencyFk: currency.id, dated: date, value: rate} + ); + } + } } } } diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/toUnbook.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/toUnbook.spec.js new file mode 100644 index 000000000..b7d98e307 --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/specs/toUnbook.spec.js @@ -0,0 +1,32 @@ +const models = require('vn-loopback/server/server').models; + +describe('invoiceIn toUnbook()', () => { + it('should check that invoiceIn is unbooked', async() => { + const userId = 1; + const ctx = { + req: { + + accessToken: {userId: userId}, + headers: {origin: 'http://localhost:5000'}, + } + }; + const invoiceInId = 1; + const tx = await models.InvoiceIn.beginTransaction({}); + const options = {transaction: tx}; + + try { + await models.InvoiceIn.toBook(ctx, invoiceInId, options); + const bookEntry = await models.InvoiceIn.toUnbook(ctx, invoiceInId, options); + const invoiceIn = await models.InvoiceIn.findById(invoiceInId, null, options); + + expect(bookEntry.accountingEntries).toEqual(4); + expect(bookEntry.isLinked).toBeFalsy(); + expect(invoiceIn.isBooked).toEqual(false); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); diff --git a/modules/invoiceIn/back/methods/invoice-in/toUnbook.js b/modules/invoiceIn/back/methods/invoice-in/toUnbook.js new file mode 100644 index 000000000..a697e9ddc --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/toUnbook.js @@ -0,0 +1,80 @@ +module.exports = Self => { + Self.remoteMethodCtx('toUnbook', { + description: 'To unbook the invoiceIn', + accessType: 'WRITE', + accepts: { + arg: 'id', + type: 'number', + required: true, + description: 'The invoiceIn id', + http: {source: 'path'} + }, + returns: { + type: 'object', + root: true + }, + http: { + path: '/:id/toUnbook', + verb: 'POST' + } + }); + + Self.toUnbook = async(ctx, invoiceInId, options) => { + let tx; + const models = Self.app.models; + const myOptions = {userId: ctx.req.accessToken.userId}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + let isLinked; + let accountingEntries; + + let bookEntry = await models.Xdiario.findOne({ + fields: ['ASIEN'], + where: { + and: [ + {key: invoiceInId}, + {enlazado: false}, + {enlazadoSage: false} + ] + } + }, myOptions); + + let asien = bookEntry?.ASIEN; + if (asien) { + accountingEntries = await models.Xdiario.count({ASIEN: asien}, myOptions); + + await models.Xdiario.destroyAll({ASIEN: asien}, myOptions); + await Self.updateAll({id: invoiceInId}, {isBooked: false}, myOptions); + } else { + const linkedBookEntry = await models.Xdiario.findOne({ + fields: ['ASIEN'], + where: { + key: invoiceInId, + and: [{or: [{enlazado: true, enlazadoSage: true}]}] + } + }, myOptions); + + asien = linkedBookEntry?.ASIEN; + isLinked = true; + } + if (tx) await tx.commit(); + + return { + isLinked, + bookEntry: asien, + accountingEntries + }; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/invoiceIn/back/methods/invoice-in/updateInvoiceIn.js b/modules/invoiceIn/back/methods/invoice-in/updateInvoiceIn.js new file mode 100644 index 000000000..92a1ba8ee --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/updateInvoiceIn.js @@ -0,0 +1,104 @@ +module.exports = Self => { + Self.remoteMethodCtx('updateInvoiceIn', { + description: 'To update the invoiceIn attributes', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The invoiceIn id', + http: {source: 'path'} + }, { + arg: 'supplierFk', + type: 'number', + required: true + }, { + arg: 'supplierRef', + type: 'any', + }, { + arg: 'issued', + type: 'any', + }, { + arg: 'operated', + type: 'any', + }, { + arg: 'deductibleExpenseFk', + type: 'any', + }, { + arg: 'dmsFk', + type: 'any', + }, { + arg: 'bookEntried', + type: 'any', + }, { + arg: 'booked', + type: 'any', + }, { + arg: 'currencyFk', + type: 'number', + required: true + }, { + arg: 'companyFk', + type: 'any', + }, { + arg: 'withholdingSageFk', + type: 'any', + }, + ], + returns: { + type: 'object', + root: true + }, + http: { + path: '/:id/updateInvoiceIn', + verb: 'PATCH' + } + }); + + Self.updateInvoiceIn = async(ctx, + id, + supplierFk, + supplierRef, + issued, + operated, + deductibleExpenseFk, + dmsFk, + bookEntried, + booked, + currencyFk, + companyFk, + withholdingSageFk, + options + ) => { + let tx; + const myOptions = {userId: ctx.req.accessToken.userId}; + + if (typeof options == 'object') Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const invoiceIn = await Self.findById(id, null, myOptions); + invoiceIn.updateAttributes({supplierFk, + supplierRef, + issued, + operated, + deductibleExpenseFk, + dmsFk, + bookEntried, + booked, + currencyFk, + companyFk, + withholdingSageFk + }, myOptions); + if (tx) await tx.commit(); + return invoiceIn; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js index 31cdc1abe..1e69c0ef8 100644 --- a/modules/invoiceIn/back/models/invoice-in.js +++ b/modules/invoiceIn/back/models/invoice-in.js @@ -11,6 +11,8 @@ module.exports = Self => { require('../methods/invoice-in/getSerial')(Self); require('../methods/invoice-in/corrective')(Self); require('../methods/invoice-in/exchangeRateUpdate')(Self); + require('../methods/invoice-in/toUnbook')(Self); + require('../methods/invoice-in/updateInvoiceIn')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_ROW_IS_REFERENCED_2' && err.sqlMessage.includes('vehicleInvoiceIn')) diff --git a/modules/invoiceIn/front/basic-data/index.html b/modules/invoiceIn/front/basic-data/index.html index a22abbb33..fbb9b05a2 100644 --- a/modules/invoiceIn/front/basic-data/index.html +++ b/modules/invoiceIn/front/basic-data/index.html @@ -1,4 +1,4 @@ - + { Self.remoteMethodCtx('download', { @@ -66,7 +67,7 @@ module.exports = Self => { console.error(err); }); - if (process.env.NODE_ENV == 'test') { + if (!isProduction()) { try { await fs.access(file.path); } catch (error) { diff --git a/modules/invoiceOut/back/models/invoice-out.js b/modules/invoiceOut/back/models/invoice-out.js index e4fcc1a69..b0e05b626 100644 --- a/modules/invoiceOut/back/models/invoice-out.js +++ b/modules/invoiceOut/back/models/invoice-out.js @@ -1,6 +1,7 @@ const print = require('vn-print'); const path = require('path'); const UserError = require('vn-loopback/util/user-error'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { require('../methods/invoiceOut/filter')(Self); @@ -59,7 +60,7 @@ module.exports = Self => { hasPdf: true }, options); - if (process.env.NODE_ENV !== 'test') { + if (isProduction()) { await print.storage.write(buffer, { type: 'invoice', path: pdfFile.path, diff --git a/modules/invoiceOut/front/index/manual/index.html b/modules/invoiceOut/front/index/manual/index.html index 5872911e4..3b991618d 100644 --- a/modules/invoiceOut/front/index/manual/index.html +++ b/modules/invoiceOut/front/index/manual/index.html @@ -27,7 +27,7 @@ { accessType: 'READ', accepts: [{ arg: 'barcode', - type: 'number', + type: 'string', required: true, description: 'barcode' }], @@ -18,7 +18,7 @@ module.exports = Self => { } }); - Self.toItem = async(barcode, options) => { + Self.toItem = async (barcode, options) => { const myOptions = {}; if (typeof options == 'object') diff --git a/modules/item/back/methods/item/specs/getBalance.spec.js b/modules/item/back/methods/item/specs/getBalance.spec.js index 5e5148595..728b5f33e 100644 --- a/modules/item/back/methods/item/specs/getBalance.spec.js +++ b/modules/item/back/methods/item/specs/getBalance.spec.js @@ -64,7 +64,7 @@ describe('item getBalance()', () => { const secondItemBalance = await models.Item.getBalance(ctx, secondFilter, options); expect(firstItemBalance[9].claimFk).toEqual(null); - expect(secondItemBalance[4].claimFk).toEqual(2); + expect(secondItemBalance[7].claimFk).toEqual(2); await tx.rollback(); } catch (e) { diff --git a/modules/item/back/models/expense.json b/modules/item/back/models/expense.json index 468063602..e661cbc66 100644 --- a/modules/item/back/models/expense.json +++ b/modules/item/back/models/expense.json @@ -9,7 +9,7 @@ "properties": { "id": { "id": true, - "type": "number", + "type": "string", "description": "Identifier" }, "name": { diff --git a/modules/item/back/models/item.json b/modules/item/back/models/item.json index 9d48dcbfb..10cff3e04 100644 --- a/modules/item/back/models/item.json +++ b/modules/item/back/models/item.json @@ -155,6 +155,9 @@ "minQuantity": { "type": "number", "description": "Min quantity" + }, + "photoMotivation": { + "type": "string" } }, "relations": { diff --git a/modules/mdb/back/methods/mdbVersion/upload.js b/modules/mdb/back/methods/mdbVersion/upload.js index 58fc46abb..64de72679 100644 --- a/modules/mdb/back/methods/mdbVersion/upload.js +++ b/modules/mdb/back/methods/mdbVersion/upload.js @@ -1,6 +1,7 @@ const fs = require('fs-extra'); const path = require('path'); const UserError = require('vn-loopback/util/user-error'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethodCtx('upload', { @@ -111,7 +112,7 @@ module.exports = Self => { const destinationFile = path.join( accessContainer.client.root, accessContainer.name, appName, `${toVersion}.7z`); - if (process.env.NODE_ENV == 'test') + if (!isProduction()) await fs.unlink(srcFile); else { await fs.move(srcFile, destinationFile, { diff --git a/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js b/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js index bdafd14e2..c3da7f08b 100644 --- a/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js +++ b/modules/monitor/back/methods/sales-monitor/specs/salesFilter.spec.js @@ -151,7 +151,7 @@ describe('SalesMonitor salesFilter()', () => { const result = await models.SalesMonitor.salesFilter(ctx, filter, options); const firstRow = result[0]; - expect(result.length).toEqual(12); + expect(result.length).toEqual(15); expect(firstRow.alertLevel).not.toEqual(0); await tx.rollback(); diff --git a/modules/shelving/back/model-config.json b/modules/shelving/back/model-config.json index 89a0832b0..6f3ffb5ea 100644 --- a/modules/shelving/back/model-config.json +++ b/modules/shelving/back/model-config.json @@ -11,6 +11,12 @@ "Sector": { "dataSource": "vn" }, + "SectorCollection": { + "dataSource": "vn" + }, + "SectorCollectionSaleGroup": { + "dataSource": "vn" + }, "Train": { "dataSource": "vn" } diff --git a/modules/shelving/back/models/sectorCollection.json b/modules/shelving/back/models/sectorCollection.json new file mode 100644 index 000000000..bf2cc7985 --- /dev/null +++ b/modules/shelving/back/models/sectorCollection.json @@ -0,0 +1,24 @@ +{ + "name": "SectorCollection", + "base": "VnModel", + "options": { + "mysql": { + "table": "sectorCollection" + } + }, + "properties": { + "id": { + "type": "number", + "id": true + }, + "created": { + "type": "date" + }, + "userFk": { + "type": "number" + }, + "sectorFk": { + "type": "number" + } + } +} diff --git a/modules/shelving/back/models/sectorCollectionSaleGroup.json b/modules/shelving/back/models/sectorCollectionSaleGroup.json new file mode 100644 index 000000000..421bdc885 --- /dev/null +++ b/modules/shelving/back/models/sectorCollectionSaleGroup.json @@ -0,0 +1,30 @@ +{ + "name": "SectorCollectionSaleGroup", + "base": "VnModel", + "options": { + "mysql": { + "table": "sectorCollectionSaleGroup" + } + }, + "properties": { + "id": { + "type": "number", + "id": true + }, + "created": { + "type": "date" + } + }, + "relations": { + "sectorCollection": { + "type": "belongsTo", + "model": "SectorCollection", + "foreignKey": "sectorCollectionFk" + }, + "saleGroup": { + "type": "belongsTo", + "model": "SaleGroup", + "foreignKey": "saleGroupFk" + } + } +} diff --git a/modules/supplier/back/methods/supplier/getItemsPackaging.js b/modules/supplier/back/methods/supplier/getItemsPackaging.js index c06195a55..8a27c89c4 100644 --- a/modules/supplier/back/methods/supplier/getItemsPackaging.js +++ b/modules/supplier/back/methods/supplier/getItemsPackaging.js @@ -33,7 +33,7 @@ module.exports = Self => { JOIN vn.item i ON i.id = b.itemFk WHERE e.id = ? AND e.supplierFk = ? GROUP BY i.id - ) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal, et.printedStickers + ) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal, et.printedStickers, ic.url FROM vn.buy b JOIN vn.item i ON i.id = b.itemFk JOIN vn.entry e ON e.id = b.entryFk @@ -41,6 +41,7 @@ module.exports = Self => { JOIN vn.buyConfig bc ON bc.monthsAgo JOIN vn.travel t ON t.id = e.travelFk LEFT JOIN entryTmp et ON et.id = i.id + JOIN hedera.imageConfig ic WHERE e.supplierFk = ? AND i.family IN ('EMB', 'CONT') AND b.created > (util.VN_CURDATE() - INTERVAL bc.monthsAgo MONTH) diff --git a/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js b/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js index 3d37221c4..3d39ea278 100644 --- a/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js +++ b/modules/ticket/back/methods/ticket-tracking/specs/setDelivered.spec.js @@ -2,10 +2,12 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); describe('ticket setDelivered()', () => { - const userId = 50; + const userId = 49; const activeCtx = { accessToken: {userId: userId}, + __: value => value }; + const ctx = {req: activeCtx}; beforeAll(async() => { spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ @@ -19,8 +21,6 @@ describe('ticket setDelivered()', () => { try { const options = {transaction: tx}; - const ctx = {req: {accessToken: {userId: 49}}}; - const originalTicketOne = await models.Ticket.findById(8, null, options); const originalTicketTwo = await models.Ticket.findById(10, null, options); diff --git a/modules/ticket/back/methods/ticket/addSale.js b/modules/ticket/back/methods/ticket/addSale.js index 826de6e12..8dc7a633c 100644 --- a/modules/ticket/back/methods/ticket/addSale.js +++ b/modules/ticket/back/methods/ticket/addSale.js @@ -10,8 +10,8 @@ module.exports = Self => { http: {source: 'path'} }, { - arg: 'itemId', - type: 'number', + arg: 'barcode', + type: 'any', required: true }, { @@ -29,7 +29,7 @@ module.exports = Self => { } }); - Self.addSale = async(ctx, id, itemId, quantity, options) => { + Self.addSale = async(ctx, id, barcode, quantity, options) => { const $t = ctx.req.__; // $translate const models = Self.app.models; const myOptions = {userId: ctx.req.accessToken.userId}; @@ -46,7 +46,9 @@ module.exports = Self => { try { await models.Ticket.isEditableOrThrow(ctx, id, myOptions); + const itemId = await models.ItemBarcode.toItem(barcode, myOptions); const item = await models.Item.findById(itemId, null, myOptions); + const ticket = await models.Ticket.findById(id, { include: { relation: 'client', diff --git a/modules/ticket/back/methods/ticket/addSaleByCode.js b/modules/ticket/back/methods/ticket/addSaleByCode.js deleted file mode 100644 index ca3d2cb07..000000000 --- a/modules/ticket/back/methods/ticket/addSaleByCode.js +++ /dev/null @@ -1,56 +0,0 @@ -const UserError = require('vn-loopback/util/user-error'); -module.exports = Self => { - Self.remoteMethodCtx('addSaleByCode', { - description: 'Add a collection', - accessType: 'WRITE', - accepts: [ - { - arg: 'barcode', - type: 'string', - required: true - }, { - arg: 'quantity', - type: 'number', - required: true - }, { - arg: 'ticketFk', - type: 'number', - required: true - }, { - arg: 'warehouseFk', - type: 'number', - required: true - }, - - ], - http: { - path: `/addSaleByCode`, - verb: 'POST' - }, - }); - - Self.addSaleByCode = async(ctx, barcode, quantity, ticketFk, warehouseFk, options) => { - 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 [[item]] = await Self.rawSql('CALL vn.item_getInfo(?,?)', [barcode, warehouseFk], myOptions); - if (!item?.available) throw new UserError('We do not have availability for the selected item'); - - await Self.rawSql('CALL vn.collection_addItem(?, ?, ?)', [item.id, quantity, ticketFk], myOptions); - - if (tx) await tx.commit(); - } catch (e) { - if (tx) await tx.rollback(); - throw e; - } - }; -}; diff --git a/modules/ticket/back/methods/ticket/isEditableOrThrow.js b/modules/ticket/back/methods/ticket/isEditableOrThrow.js index 16cff84f1..555063093 100644 --- a/modules/ticket/back/methods/ticket/isEditableOrThrow.js +++ b/modules/ticket/back/methods/ticket/isEditableOrThrow.js @@ -8,18 +8,13 @@ module.exports = Self => { if (typeof options == 'object') Object.assign(myOptions, options); - const state = await models.TicketState.findOne({ - where: {ticketFk: id} - }, myOptions); - + const state = await models.TicketState.findOne({where: {ticketFk: id}}, myOptions); const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*'); + const isProductionReviewer = await models.ACL.checkAccessAcl(ctx, 'Sale', 'isInPreparing', '*'); const canEditWeeklyTicket = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'canEditWeekly', 'WRITE'); const alertLevel = state ? state.alertLevel : null; const ticket = await models.Ticket.findById(id, { - fields: ['clientFk'], - include: { - relation: 'client' - } + fields: ['clientFk'], include: {relation: 'client'} }, myOptions); const isLocked = await models.Ticket.isLocked(id, myOptions); @@ -29,10 +24,24 @@ module.exports = Self => { const isNormalClient = ticket && ticket.client().typeFk == 'normal'; const isEditable = !(alertLevelGreaterThanZero && isNormalClient); + const ticketCollection = await models.TicketCollection.findOne({ + include: {relation: 'collection'}, where: {ticketFk: id} + }, myOptions); + let isOwner = ticketCollection?.collection()?.workerFk === ctx.req.accessToken.userId; + + if (!isOwner) { + const saleGroup = await models.SaleGroup.findOne({fields: ['id'], where: {ticketFk: id}}, myOptions); + const sectorCollectionSaleGroup = saleGroup && await models.SectorCollectionSaleGroup.findOne({ + include: {relation: 'sectorCollection'}, where: {saleGroupFk: saleGroup.id} + }, myOptions); + + isOwner = sectorCollectionSaleGroup?.sectorCollection()?.userFk === ctx.req.accessToken.userId; + } + if (!ticket) throw new ForbiddenError(`The ticket doesn't exist.`); - if (!isEditable && !isRoleAdvanced) + if (!isEditable && !isRoleAdvanced && !isProductionReviewer && !isOwner) throw new ForbiddenError(`This ticket is not editable.`); if (isLocked && !isWeekly) diff --git a/modules/ticket/back/methods/ticket/specs/addSaleByCode.spec.js b/modules/ticket/back/methods/ticket/specs/addSaleByCode.spec.js deleted file mode 100644 index b97139178..000000000 --- a/modules/ticket/back/methods/ticket/specs/addSaleByCode.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -const {models} = require('vn-loopback/server/server'); -const LoopBackContext = require('loopback-context'); - -describe('Ticket addSaleByCode()', () => { - const quantity = 3; - const ticketFk = 13; - const warehouseFk = 1; - beforeAll(async() => { - activeCtx = { - req: { - accessToken: {userId: 9}, - headers: {origin: 'http://localhost'}, - __: value => value - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ - active: activeCtx - }); - }); - - it('should add a new sale', async() => { - const tx = await models.Ticket.beginTransaction({}); - - try { - const options = {transaction: tx}; - const code = '1111111111'; - - const salesBefore = await models.Sale.find(null, options); - await models.Ticket.addSaleByCode(activeCtx, code, quantity, ticketFk, warehouseFk, options); - const salesAfter = await models.Sale.find(null, options); - - expect(salesAfter.length).toEqual(salesBefore.length + 1); - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } - }); -}); diff --git a/modules/ticket/back/methods/ticket/specs/filter.spec.js b/modules/ticket/back/methods/ticket/specs/filter.spec.js index e495a41f5..8008acfaf 100644 --- a/modules/ticket/back/methods/ticket/specs/filter.spec.js +++ b/modules/ticket/back/methods/ticket/specs/filter.spec.js @@ -68,7 +68,7 @@ describe('ticket filter()', () => { const filter = {}; const result = await models.Ticket.filter(ctx, filter, options); - expect(result.length).toEqual(7); + expect(result.length).toEqual(10); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js index e5c06b6dd..7dc1c8ed2 100644 --- a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js +++ b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js @@ -42,7 +42,7 @@ describe('sale priceDifference()', () => { try { const options = {transaction: tx}; - const ctx = {req: {accessToken: {userId: 1106}}}; + const ctx = {req: {accessToken: {userId: 1105}}}; ctx.args = { id: 1, landed: Date.vnNew(), @@ -84,7 +84,7 @@ describe('sale priceDifference()', () => { const {items} = await models.Ticket.priceDifference(ctx, options); - expect(items[0].movable).toEqual(410); + expect(items[0].movable).toEqual(386); expect(items[1].movable).toEqual(1810); await tx.rollback(); diff --git a/modules/ticket/back/methods/ticket/specs/state.spec.js b/modules/ticket/back/methods/ticket/specs/state.spec.js index 947e72b79..d908aa2ef 100644 --- a/modules/ticket/back/methods/ticket/specs/state.spec.js +++ b/modules/ticket/back/methods/ticket/specs/state.spec.js @@ -7,6 +7,7 @@ describe('ticket state()', () => { const productionId = 49; const activeCtx = { accessToken: {userId: 9}, + __: value => value }; const ctx = {req: activeCtx}; const now = Date.vnNew(); @@ -88,7 +89,8 @@ describe('ticket state()', () => { const ticket = await models.Ticket.create(sampleTicket, options); activeCtx.accessToken.userId = productionId; - const params = {ticketFk: ticket.id, stateFk: 3}; + const stateOk = await models.State.findOne({where: {code: 'OK'}}, options); + const params = {ticketFk: ticket.id, stateFk: stateOk.id}; const ticketTracking = await models.Ticket.state(ctx, params, options); @@ -112,16 +114,68 @@ describe('ticket state()', () => { const options = {transaction: tx}; const ticket = await models.Ticket.create(sampleTicket, options); - const ctx = {req: {accessToken: {userId: 18}}}; + activeCtx.accessToken.userId = salesPersonId; const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options); - const params = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1}; - const res = await models.Ticket.state(ctx, params, options); + const paramsAssigned = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1}; + const resAssigned = await models.Ticket.state(ctx, paramsAssigned, options); - expect(res.ticketFk).toBe(params.ticketFk); - expect(res.stateFk).toBe(params.stateFk); - expect(res.userFk).toBe(params.userFk); - expect(res.userFk).toBe(1); - expect(res.id).toBeDefined(); + expect(resAssigned.ticketFk).toBe(paramsAssigned.ticketFk); + expect(resAssigned.stateFk).toBe(paramsAssigned.stateFk); + expect(resAssigned.userFk).toBe(paramsAssigned.userFk); + expect(resAssigned.userFk).toBe(1); + expect(resAssigned.id).toBeDefined(); + + activeCtx.accessToken.userId = productionId; + const packedState = await models.State.findOne({where: {code: 'PACKED'}}, options); + const paramsPacked = {ticketFk: ticket.id, stateFk: packedState.id, userFk: salesPersonId}; + const resPacked = await models.Ticket.state(ctx, paramsPacked, options); + + expect(resPacked.stateFk).toBe(paramsPacked.stateFk); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('Should equalize the quantities of quantity and originalQuantity' + + ' if they are different', async() => { + const tx = await models.TicketTracking.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const ticket = await models.Ticket.create(sampleTicket, options); + activeCtx.accessToken.userId = salesPersonId; + + const sampleSale = { + ticketFk: ticket.id, + itemFk: 1, + concept: 'Test', + quantity: 10, + originalQuantity: 6 + }; + await models.Sale.create(sampleSale, options); + const assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}}, options); + const paramsAssigned = {ticketFk: ticket.id, stateFk: assignedState.id, userFk: 1}; + const resAssigned = await models.Ticket.state(ctx, paramsAssigned, options); + + expect(resAssigned.ticketFk).toBe(paramsAssigned.ticketFk); + expect(resAssigned.stateFk).toBe(paramsAssigned.stateFk); + expect(resAssigned.userFk).toBe(paramsAssigned.userFk); + expect(resAssigned.userFk).toBe(1); + expect(resAssigned.id).toBeDefined(); + + activeCtx.accessToken.userId = productionId; + const packedState = await models.State.findOne({where: {code: 'PACKED'}}, options); + const paramsPacked = {ticketFk: ticket.id, stateFk: packedState.id, userFk: salesPersonId}; + const resPacked = await models.Ticket.state(ctx, paramsPacked, options); + + const sale = await models.Sale.findOne({where: {ticketFk: ticket.id}}, options); + + expect(resPacked.stateFk).toBe(paramsPacked.stateFk); + expect(sale.quantity).toBe(sale.originalQuantity); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/methods/ticket/state.js b/modules/ticket/back/methods/ticket/state.js index fea9475f8..9b0b862f2 100644 --- a/modules/ticket/back/methods/ticket/state.js +++ b/modules/ticket/back/methods/ticket/state.js @@ -64,7 +64,63 @@ module.exports = Self => { if ((ticketState && !oldStateAllowed) || !newStateAllowed) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); - await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [params.ticketFk, params.code], myOptions); + const ticket = await models.Ticket.findById(params.ticketFk, { + include: [{ + relation: 'client', + scope: { + fields: ['salesPersonFk'] + } + }], + fields: ['id', 'clientFk'] + }, myOptions); + + const salesPersonFk = ticket.client().salesPersonFk; + if (salesPersonFk) { + const sales = await Self.rawSql(` + SELECT DISTINCT s.id, + s.itemFk, + s.concept, + s.originalQuantity AS oldQuantity, + s.quantity AS newQuantity + FROM vn.sale s + JOIN vn.saleTracking st ON st.saleFk = s.id + JOIN vn.ticket t ON t.id = s.ticketFk + JOIN vn.client c ON c.id = t.clientFk + JOIN vn.ticketState ts ON ts.ticketFk = t.id + JOIN vn.state s2 ON s2.id = ts.stateFk + WHERE s.ticketFk = ? + AND st.isChecked + AND s.originalQuantity IS NOT NULL + AND s.originalQuantity <> s.quantity + AND s2.\`order\` < (SELECT \`order\` FROM vn.state WHERE code = 'CHECKED') + ORDER BY st.created DESC + `, [params.ticketFk], myOptions); + + let changes = ''; + const url = await models.Url.getUrl(); + const $t = ctx.req.__; + for (let sale of sales) { + changes += `\r\n-` + $t('Changes in sales', { + itemId: sale.itemFk, + concept: sale.concept, + oldQuantity: sale.oldQuantity, + newQuantity: sale.newQuantity, + itemUrl: `${url}item/${sale.itemFk}/summary` + }); + const currentSale = await models.Sale.findById(sale.id, null, myOptions); + await currentSale.updateAttributes({ + originalQuantity: currentSale.quantity + }, myOptions); + } + + const message = $t('Changed sale quantity', { + ticketId: ticket.id, + changes: changes, + ticketUrl: `${url}ticket/${ticket.id}/sale` + }); + await models.Chat.sendCheckingPresence(ctx, salesPersonFk, message, myOptions); + } + await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [ticket.id, params.code], myOptions); const ticketTracking = await models.TicketTracking.findOne({ where: {ticketFk: params.ticketFk}, diff --git a/modules/ticket/back/models/saleGroup.json b/modules/ticket/back/models/saleGroup.json index d5cf82cb5..aa78b4167 100644 --- a/modules/ticket/back/models/saleGroup.json +++ b/modules/ticket/back/models/saleGroup.json @@ -14,6 +14,9 @@ }, "parkingFk": { "type": "number" + }, + "ticketFk": { + "type": "number" } }, "relations": { diff --git a/modules/ticket/back/models/specs/sale.spec.js b/modules/ticket/back/models/specs/sale.spec.js index d078dc8e2..1aa40802b 100644 --- a/modules/ticket/back/models/specs/sale.spec.js +++ b/modules/ticket/back/models/specs/sale.spec.js @@ -1,361 +1,318 @@ /* eslint max-len: ["error", { "code": 150 }]*/ - -const models = require('vn-loopback/server/server').models; +const {models} = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); describe('sale model ', () => { - const ctx = { - req: { - accessToken: {userId: 9}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - function getActiveCtx(userId) { - return { - active: { - accessToken: {userId}, - http: { - req: { - headers: {origin: 'http://localhost'} - } - } - } - }; + const developerId = 9; + const buyerId = 35; + const employeeId = 1; + const productionId = 49; + const salesPersonId = 18; + const reviewerId = 130; + + const barcode = '4444444444'; + const ticketCollectionProd = 34; + const ticketCollectionSalesPerson = 35; + const previaTicketSalesPerson = 36; + const previaTicketProd = 37; + const notEditableError = 'This ticket is not editable.'; + + const ctx = getCtx(developerId); + let tx; + let opts; + + function getCtx(userId, active = false) { + const accessToken = {userId}; + const headers = {origin: 'localhost:5000'}; + if (!active) return {req: {accessToken, headers, __: () => {}}}; + return {active: {accessToken, http: {req: {headers}}}}; } + beforeEach(async() => { + tx = await models.Sale.beginTransaction({}); + opts = {transaction: tx}; + }); + + afterEach(async() => await tx.rollback()); + describe('quantity field ', () => { it('should add quantity if the quantity is greater than it should be and is role advanced', async() => { const saleId = 17; - const buyerId = 35; - const ctx = { - req: { - accessToken: {userId: buyerId}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - const tx = await models.Sale.beginTransaction({}); - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId)); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + const ctx = getCtx(buyerId); + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(buyerId, true)); + spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => { if (sqlStatement.includes('catalog_calcFromItem')) { sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT 100 as available;`; params = null; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - try { - const options = {transaction: tx}; + const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*'); - const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*'); + expect(isRoleAdvanced).toEqual(true); - expect(isRoleAdvanced).toEqual(true); + const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); - const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + expect(originalLine.quantity).toEqual(30); - expect(originalLine.quantity).toEqual(30); + const newQuantity = originalLine.quantity + 1; + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); - const newQuantity = originalLine.quantity + 1; - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); - const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); - - expect(modifiedLine.quantity).toEqual(newQuantity); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + expect(modifiedLine.quantity).toEqual(newQuantity); }); it('should update the quantity of a given sale current line', async() => { - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(developerId, true)); - const tx = await models.Sale.beginTransaction({}); const saleId = 25; const newQuantity = 4; - try { - const options = {transaction: tx}; + const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); - const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + expect(originalLine.quantity).toEqual(20); - expect(originalLine.quantity).toEqual(20); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); - const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); - - expect(modifiedLine.quantity).toEqual(newQuantity); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + expect(modifiedLine.quantity).toEqual(newQuantity); }); it('should throw an error if the quantity is negative and it is not a refund ticket', async() => { - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true)); const saleId = 17; const newQuantity = -10; - const tx = await models.Sale.beginTransaction({}); - - let error; try { - const options = {transaction: tx}; - - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); - - await tx.rollback(); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); } catch (e) { - await tx.rollback(); - error = e; + expect(e).toEqual(new Error('You can only add negative amounts in refund tickets')); } - - expect(error).toEqual(new Error('You can only add negative amounts in refund tickets')); }); it('should update a negative quantity when is a ticket refund', async() => { - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(developerId, true)); - const tx = await models.Sale.beginTransaction({}); const saleId = 32; const newQuantity = -10; - try { - const options = {transaction: tx}; + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); - const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); - - expect(modifiedLine.quantity).toEqual(newQuantity); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + expect(modifiedLine.quantity).toEqual(newQuantity); }); it('should throw an error if the quantity is less than the minimum quantity of the item', async() => { - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true)); - const tx = await models.Sale.beginTransaction({}); const itemId = 2; const saleId = 17; const minQuantity = 30; const newQuantity = minQuantity - 1; - let error; try { - const options = {transaction: tx}; - - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + const item = await models.Item.findById(itemId, null, opts); + await item.updateAttribute('minQuantity', minQuantity, opts); + spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => { if (sqlStatement.includes('catalog_calcFromItem')) { sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT 100 as available;`; params = null; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); - - await tx.rollback(); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); } catch (e) { - await tx.rollback(); - error = e; + expect(e).toEqual(new Error('The amount cannot be less than the minimum')); } - - expect(error).toEqual(new Error('The amount cannot be less than the minimum')); }); it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => { - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true)); - const tx = await models.Sale.beginTransaction({}); const itemId = 2; const saleId = 17; const minQuantity = 30; const newQuantity = minQuantity - 1; - try { - const options = {transaction: tx}; + const item = await models.Item.findById(itemId, null, opts); + await item.updateAttribute('minQuantity', minQuantity, opts); - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { - if (sqlStatement.includes('catalog_calcFromItem')) { - sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY + spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => { + if (sqlStatement.includes('catalog_calcFromItem')) { + sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;`; - params = null; - } - return models.Ticket.rawSql(sqlStatement, params, options); - }); + params = null; + } + return models.Ticket.rawSql(sqlStatement, params, opts); + }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); }); describe('newPrice', () => { it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => { - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true)); - const tx = await models.Sale.beginTransaction({}); const itemId = 2; const saleId = 17; const minQuantity = 30; const newQuantity = 31; - try { - const options = {transaction: tx}; - - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { - if (sqlStatement.includes('catalog_calcFromItem')) { - sqlStatement = ` + const item = await models.Item.findById(itemId, null, opts); + await item.updateAttribute('minQuantity', minQuantity, opts); + spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => { + if (sqlStatement.includes('catalog_calcFromItem')) { + sqlStatement = ` CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available; CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 7.07 as price;`; - params = null; - } - return models.Ticket.rawSql(sqlStatement, params, options); - }); + params = null; + } + return models.Ticket.rawSql(sqlStatement, params, opts); + }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); }); it('should increase quantity when the new price is lower than the previous one', async() => { - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true)); - const tx = await models.Sale.beginTransaction({}); const itemId = 2; const saleId = 17; const minQuantity = 30; const newQuantity = 31; - try { - const options = {transaction: tx}; - - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { - if (sqlStatement.includes('catalog_calcFromItem')) { - sqlStatement = ` + const item = await models.Item.findById(itemId, null, opts); + await item.updateAttribute('minQuantity', minQuantity, opts); + spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => { + if (sqlStatement.includes('catalog_calcFromItem')) { + sqlStatement = ` CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available; CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 1 as price;`; - params = null; - } - return models.Ticket.rawSql(sqlStatement, params, options); - }); + params = null; + } + return models.Ticket.rawSql(sqlStatement, params, opts); + }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); }); it('should throw error when increase quantity and the new price is higher than the previous one', async() => { - const ctx = { - req: { - accessToken: {userId: 1}, - headers: {origin: 'localhost:5000'}, - __: () => {} - } - }; - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1)); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true)); - const tx = await models.Sale.beginTransaction({}); const itemId = 2; const saleId = 17; const minQuantity = 30; const newQuantity = 31; - let error; try { - const options = {transaction: tx}; - - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + const item = await models.Item.findById(itemId, null, opts); + await item.updateAttribute('minQuantity', minQuantity, opts); + spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => { if (sqlStatement.includes('catalog_calcFromItem')) { sqlStatement = ` CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available; CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 100000 as price;`; params = null; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); - - await tx.rollback(); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); } catch (e) { - await tx.rollback(); - error = e; + expect(e).toEqual(new Error('The price of the item changed')); } - - expect(error).toEqual(new Error('The price of the item changed')); }); }); }); + + describe('add a sale from a collection or previa ticket', () => { + it('if is allocated to them and alert level higher than 0 as Production role', async() => { + const ctx = getCtx(productionId); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(productionId, true)); + + const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionProd}, opts); + await models.Ticket.addSale(ctx, ticketCollectionProd, barcode, 20, opts); + const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionProd}, opts); + + expect(afterSalesCollection).toEqual(beforeSalesCollection + 1); + + const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketProd}, opts); + await models.Ticket.addSale(ctx, previaTicketProd, barcode, 20, opts); + const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketProd}, opts); + + expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1); + }); + + it('should throw an error if is not allocated to them and alert level higher than 0 as Production role', async() => { + const ctx = getCtx(productionId); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(productionId, true)); + + try { + await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts); + } catch ({message}) { + expect(message).toEqual(notEditableError); + } + + try { + await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts); + } catch ({message}) { + expect(message).toEqual(notEditableError); + } + }); + + it('if is allocated to them and alert level higher than 0 as salesPerson role', async() => { + const ctx = getCtx(salesPersonId); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(salesPersonId, true)); + + const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts); + await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts); + const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts); + + expect(afterSalesCollection).toEqual(beforeSalesCollection + 1); + + const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts); + await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts); + const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts); + + expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1); + }); + + it('should throw an error if is not allocated to them and alert level higher than 0 as salesPerson role', async() => { + const ctx = getCtx(salesPersonId); + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(salesPersonId, true)); + + try { + await models.Ticket.addSale(ctx, ticketCollectionProd, barcode, 20, opts); + } catch ({message}) { + expect(message).toEqual(notEditableError); + } + }); + + it('if is a reviewer', async() => { + const ctx = getCtx(reviewerId); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(reviewerId, true)); + + const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts); + await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts); + const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts); + + expect(afterSalesCollection).toEqual(beforeSalesCollection + 1); + + const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts); + await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts); + const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts); + + expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1); + }); + }); }); diff --git a/modules/ticket/back/models/ticket-methods.js b/modules/ticket/back/models/ticket-methods.js index 0ae2ce3b4..5582dde5c 100644 --- a/modules/ticket/back/models/ticket-methods.js +++ b/modules/ticket/back/models/ticket-methods.js @@ -46,6 +46,5 @@ module.exports = function(Self) { require('../methods/ticket/invoiceTicketsAndPdf')(Self); require('../methods/ticket/docuwareDownload')(Self); require('../methods/ticket/myLastModified')(Self); - require('../methods/ticket/addSaleByCode')(Self); require('../methods/ticket/clone')(Self); }; diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js index 1cd5560a4..7ff8d89e3 100644 --- a/modules/ticket/front/sale/index.js +++ b/modules/ticket/front/sale/index.js @@ -476,7 +476,7 @@ class Controller extends Section { */ addSale(sale) { const data = { - itemId: sale.itemFk, + barcode: sale.itemFk, quantity: sale.quantity }; const query = `tickets/${this.ticket.id}/addSale`; diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js index fb1c925d4..8200d6b89 100644 --- a/modules/ticket/front/sale/index.spec.js +++ b/modules/ticket/front/sale/index.spec.js @@ -659,7 +659,7 @@ describe('Ticket', () => { jest.spyOn(controller, 'resetChanges').mockReturnThis(); const newSale = {itemFk: 4, quantity: 10}; - const expectedParams = {itemId: 4, quantity: 10}; + const expectedParams = {barcode: 4, quantity: 10}; const expectedResult = { id: 30, quantity: 10, diff --git a/modules/zone/back/methods/zone/specs/deleteZone.spec.js b/modules/zone/back/methods/zone/specs/deleteZone.spec.js index 968685fec..08dafd181 100644 --- a/modules/zone/back/methods/zone/specs/deleteZone.spec.js +++ b/modules/zone/back/methods/zone/specs/deleteZone.spec.js @@ -5,6 +5,7 @@ describe('zone deletezone()', () => { const userId = 9; const activeCtx = { accessToken: {userId: userId}, + __: value => value }; const ctx = {req: activeCtx}; const zoneId = 9; @@ -15,19 +16,15 @@ describe('zone deletezone()', () => { spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ active: activeCtx }); - try { - const originalTickets = await models.Ticket.find({ - where: { - zoneFk: zoneId - } - }); - ticketIDs = originalTickets.map(ticket => ticket.id); - originalTicketStates = await models.TicketState.find({where: { - ticketFk: {inq: ticketIDs}, - code: 'FIXING'}}); - } catch (error) { - console.error(error); - } + const originalTickets = await models.Ticket.find({ + where: { + zoneFk: zoneId + } + }); + ticketIDs = originalTickets.map(ticket => ticket.id); + originalTicketStates = await models.TicketState.find({where: { + ticketFk: {inq: ticketIDs}, + code: 'FIXING'}}); }); it('should delete a zone and update their tickets', async() => { diff --git a/package.json b/package.json index 468d13156..be361ce7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-back", - "version": "24.22.10", + "version": "24.24.3", "author": "Verdnatura Levante SL", "description": "Salix backend", "license": "GPL-3.0",