diff --git a/back/methods/dms/downloadFile.js b/back/methods/dms/downloadFile.js index 1b91500536..d64b15b70a 100644 --- a/back/methods/dms/downloadFile.js +++ b/back/methods/dms/downloadFile.js @@ -29,7 +29,8 @@ module.exports = Self => { http: { path: `/:id/downloadFile`, verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.downloadFile = async function(ctx, id) { diff --git a/back/methods/docuware/download.js b/back/methods/docuware/download.js index a0d72ce017..a1776cde54 100644 --- a/back/methods/docuware/download.js +++ b/back/methods/docuware/download.js @@ -42,7 +42,8 @@ module.exports = Self => { http: { path: `/:id/download`, verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.download = async function(id, fileCabinet, filter) { diff --git a/back/methods/image/download.js b/back/methods/image/download.js index 2b1a4b5465..201e16164a 100644 --- a/back/methods/image/download.js +++ b/back/methods/image/download.js @@ -47,7 +47,8 @@ module.exports = Self => { http: { path: `/:collection/:size/:id/download`, verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.download = async function(ctx, collection, size, id) { diff --git a/back/methods/notification/specs/getList.spec.js b/back/methods/notification/specs/getList.spec.js index 52ac497a56..6c60d35055 100644 --- a/back/methods/notification/specs/getList.spec.js +++ b/back/methods/notification/specs/getList.spec.js @@ -7,7 +7,7 @@ describe('NotificationSubscription getList()', () => { const notifications = await models.Notification.find({}); const totalAvailable = notifications.length - active.length; - expect(active.length).toEqual(2); + expect(active.length).toEqual(3); expect(available.length).toEqual(totalAvailable); }); }); diff --git a/back/methods/vn-user/share-token.js b/back/methods/vn-user/share-token.js new file mode 100644 index 0000000000..8efa22db42 --- /dev/null +++ b/back/methods/vn-user/share-token.js @@ -0,0 +1,27 @@ + +module.exports = Self => { + Self.remoteMethodCtx('shareToken', { + description: 'Returns token to view files or images and share it', + accessType: 'WRITE', + accepts: [], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/shareToken`, + verb: 'GET' + } + }); + + Self.shareToken = async function(ctx) { + const {accessToken: token} = ctx.req; + + const user = await Self.findById(token.userId); + const multimediaToken = await user.accessTokens.create({ + scopes: ['read:multimedia'] + }); + + return {multimediaToken}; + }; +}; diff --git a/back/methods/vn-user/specs/share-token.spec.js b/back/methods/vn-user/specs/share-token.spec.js new file mode 100644 index 0000000000..aaa83817c9 --- /dev/null +++ b/back/methods/vn-user/specs/share-token.spec.js @@ -0,0 +1,27 @@ +const {models} = require('vn-loopback/server/server'); +describe('Share Token', () => { + let ctx = null; + beforeAll(async() => { + const unAuthCtx = { + req: { + headers: {}, + connection: { + remoteAddress: '127.0.0.1' + }, + getLocale: () => 'en' + }, + args: {} + }; + let login = await models.VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare'); + let accessToken = await models.AccessToken.findById(login.token); + ctx = {req: {accessToken: accessToken}}; + }); + + it('should renew token', async() => { + const multimediaToken = await models.VnUser.shareToken(ctx); + + expect(Object.keys(multimediaToken).length).toEqual(1); + expect(multimediaToken.multimediaToken.userId).toEqual(ctx.req.accessToken.userId); + expect(multimediaToken.multimediaToken.scopes[0]).toEqual('read:multimedia'); + }); +}); diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 3a416d7e3c..b59f13ffad 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -13,6 +13,7 @@ module.exports = function(Self) { require('../methods/vn-user/privileges')(Self); require('../methods/vn-user/validate-auth')(Self); require('../methods/vn-user/renew-token')(Self); + require('../methods/vn-user/share-token')(Self); require('../methods/vn-user/update-user')(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 6396036433..5f6ac3f47a 100644 --- a/back/models/vn-user.json +++ b/back/models/vn-user.json @@ -1,129 +1,140 @@ { - "name": "VnUser", - "base": "User", - "validateUpsert": true, - "options": { - "mysql": { - "table": "account.user" - } - }, + "name": "VnUser", + "base": "User", + "validateUpsert": true, + "options": { + "mysql": { + "table": "account.user" + } + }, "mixins": { "Loggable": true }, "resetPasswordTokenTTL": "604800", - "properties": { - "id": { - "type": "number", - "id": true - }, + "properties": { + "id": { + "type": "number", + "id": true + }, "name": { - "type": "string", - "required": true - }, - "username": { - "type": "string" - }, - "roleFk": { - "type": "number", - "mysql": { - "columnName": "role" - } - }, - "nickname": { - "type": "string" - }, - "lang": { - "type": "string" - }, - "active": { - "type": "boolean" - }, - "email": { - "type": "string" - }, - "emailVerified": { - "type": "boolean" - }, - "created": { - "type": "date" - }, - "updated": { - "type": "date" - }, - "image": { - "type": "string" - }, - "hasGrant": { - "type": "boolean" - }, + "type": "string", + "required": true + }, + "username": { + "type": "string" + }, + "roleFk": { + "type": "number", + "mysql": { + "columnName": "role" + } + }, + "nickname": { + "type": "string" + }, + "lang": { + "type": "string" + }, + "active": { + "type": "boolean" + }, + "email": { + "type": "string" + }, + "emailVerified": { + "type": "boolean" + }, + "created": { + "type": "date" + }, + "updated": { + "type": "date" + }, + "image": { + "type": "string" + }, + "hasGrant": { + "type": "boolean" + }, "passExpired": { "type": "date" }, - "twoFactor": { - "type": "string" - } - }, - "relations": { - "role": { - "type": "belongsTo", - "model": "VnRole", - "foreignKey": "roleFk" - }, - "roles": { - "type": "hasMany", - "model": "RoleRole", - "foreignKey": "role", - "primaryKey": "roleFk" - }, - "emailUser": { - "type": "hasOne", - "model": "EmailUser", - "foreignKey": "userFk" - }, - "worker": { - "type": "hasOne", - "model": "Worker", - "foreignKey": "id" - }, - "userConfig": { - "type": "hasOne", - "model": "UserConfig", - "foreignKey": "userFk" - } - }, - "acls": [ - { - "property": "signIn", - "accessType": "EXECUTE", - "principalType": "ROLE", - "principalId": "$everyone", - "permission": "ALLOW" - }, { - "property": "recoverPassword", - "accessType": "EXECUTE", - "principalType": "ROLE", - "principalId": "$everyone", - "permission": "ALLOW" - }, { - "property": "validateAuth", - "accessType": "EXECUTE", - "principalType": "ROLE", - "principalId": "$everyone", - "permission": "ALLOW" - }, { - "property": "privileges", - "accessType": "*", - "principalType": "ROLE", - "principalId": "$authenticated", - "permission": "ALLOW" - }, { - "property": "renewToken", - "accessType": "WRITE", - "principalType": "ROLE", - "principalId": "$authenticated", - "permission": "ALLOW" - } - ], + "twoFactor": { + "type": "string" + } + }, + "relations": { + "role": { + "type": "belongsTo", + "model": "VnRole", + "foreignKey": "roleFk" + }, + "roles": { + "type": "hasMany", + "model": "RoleRole", + "foreignKey": "role", + "primaryKey": "roleFk" + }, + "emailUser": { + "type": "hasOne", + "model": "EmailUser", + "foreignKey": "userFk" + }, + "worker": { + "type": "hasOne", + "model": "Worker", + "foreignKey": "id" + }, + "userConfig": { + "type": "hasOne", + "model": "UserConfig", + "foreignKey": "userFk" + } + }, + "acls": [ + { + "property": "signIn", + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + }, + { + "property": "recoverPassword", + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + }, + { + "property": "validateAuth", + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + }, + { + "property": "privileges", + "accessType": "*", + "principalType": "ROLE", + "principalId": "$authenticated", + "permission": "ALLOW" + }, + { + "property": "renewToken", + "accessType": "WRITE", + "principalType": "ROLE", + "principalId": "$authenticated", + "permission": "ALLOW" + }, + { + "property": "shareToken", + "accessType": "WRITE", + "principalType": "ROLE", + "principalId": "$authenticated", + "permission": "ALLOW" + } + ], "scopes": { "preview": { "fields": [ @@ -140,7 +151,7 @@ "hasGrant", "realm", "email", - "emailVerified" + "emailVerified" ] } } diff --git a/db/dump/fixtures.after.sql b/db/dump/fixtures.after.sql index bda625a960..d7f3b22e81 100644 --- a/db/dump/fixtures.after.sql +++ b/db/dump/fixtures.after.sql @@ -70,7 +70,7 @@ UPDATE vn.supplier UPDATE `vn`.`claimRatio` SET `claimAmount` = '10' WHERE (`clientFk` = '1101'); -INSERT INTO `vn`.`agency` (`name`, `warehouseFk`, `isOwn`, `isAnyVolumeAllowed`) +INSERT INTO `vn`.`agency` (`name`, `warehouseFk`, `isOwn`, `isAnyVolumeAllowed`) VALUES ('Agencia', '1', '1', '1'), ('Otra agencia ', '1', '0', '0'); diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index d68a5d8387..c14d6eca35 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -592,13 +592,13 @@ INSERT INTO `vn`.`supplierAccount`(`id`, `supplierFk`, `iban`, `bankEntityFk`) VALUES (241, 442, 'ES111122333344111122221111', 128); -INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `sage200Company`, `expired`, `companyGroupFk`, `phytosanitary` , `clientFk`) +INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `expired`, `companyGroupFk`, `phytosanitary` , `clientFk`) VALUES - (69 , 'CCs', NULL, 30, NULL, 0, NULL, 1, NULL , NULL), - (442 , 'VNL', 241, 30, 2 , 1, NULL, 2, 'VNL Company - Plant passport' , 1101), - (567 , 'VNH', NULL, 30, NULL, 4, NULL, 1, 'VNH Company - Plant passport' , NULL), - (791 , 'FTH', NULL, 30, NULL, 3, '2015-11-30', 1, NULL , NULL), - (1381, 'ORN', NULL, 30, NULL, 7, NULL, 1, 'ORN Company - Plant passport' , NULL); + (69 , 'CCs', NULL, 30, 0, NULL, 1, NULL , NULL), + (442 , 'VNL', 241, 30, 1, NULL, 2, 'VNL Company - Plant passport' , 1101), + (567 , 'VNH', NULL, 30, 4, NULL, 1, 'VNH Company - Plant passport' , NULL), + (791 , 'FTH', NULL, 30, 3, '2015-11-30', 1, NULL , NULL), + (1381, 'ORN', NULL, 30, 7, NULL, 1, 'ORN Company - Plant passport' , NULL); INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`) VALUES @@ -1491,8 +1491,8 @@ INSERT INTO `bs`.`waste`(`buyer`, `year`, `week`, `family`, `itemFk`, `itemTypeF INSERT INTO `vn`.`buy`(`id`,`entryFk`,`itemFk`,`buyingValue`,`quantity`,`packagingFk`,`stickers`,`freightValue`,`packageValue`,`comissionValue`,`packing`,`grouping`,`groupingMode`,`location`,`price1`,`price2`,`price3`, `printedStickers`,`isChecked`,`isIgnored`,`weight`, `created`) VALUES - (1, 1, 1, 50, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 1, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)), - (2, 2, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 1, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), + (1, 1, 1, 50, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 1, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 2 MONTH), + (2, 2, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 1, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 1 MONTH), (3, 3, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 0, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE()), (4, 2, 2, 5, 450, 3, 1, 1.000, 1.000, 0.000, 10, 10, 0, NULL, 0.00, 7.30, 7.00, 0, 1, 0, 2.5, util.VN_CURDATE()), (5, 3, 3, 55, 500, 5, 1, 1.000, 1.000, 0.000, 1, 1, 0, NULL, 0.00, 78.3, 75.6, 0, 1, 0, 2.5, util.VN_CURDATE()), @@ -2809,7 +2809,8 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`) (3, 'not-main-printer-configured', 'A printer distinct than main has been configured'), (4, 'supplier-pay-method-update', 'A supplier pay method has been updated'), (5, 'modified-entry', 'An entry has been modified'), - (6, 'book-entry-deleted', 'accounting entries deleted'); + (6, 'book-entry-deleted', 'accounting entries deleted'), + (7, 'zone-included','An email to notify zoneCollisions'); INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) VALUES @@ -2819,7 +2820,8 @@ INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) (3, 9), (4, 1), (5, 9), - (6, 9); + (6, 9), + (7, 9); INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`) VALUES @@ -2836,8 +2838,8 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`) (2, 1109), (1, 9), (1, 3), - (6, 9); - + (6, 9), + (7, 9); INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`) VALUES diff --git a/db/routines/sage/procedures/pgc_add.sql b/db/routines/sage/procedures/pgc_add.sql index ebcb2d0437..78d80a9fe4 100644 --- a/db/routines/sage/procedures/pgc_add.sql +++ b/db/routines/sage/procedures/pgc_add.sql @@ -17,13 +17,13 @@ BEGIN e.id accountFk, UCASE(e.name), '' - FROM expense e + FROM vn.expense e UNION SELECT company_getCode(vCompanyFk), a.account, UCASE(a.bank), '' - FROM accounting a + FROM vn.accounting a WHERE a.isActive AND a.`account` UNION diff --git a/db/routines/vn/functions/travel_hasUniqueAwb.sql b/db/routines/vn/functions/travel_hasUniqueAwb.sql new file mode 100644 index 0000000000..e918f1a266 --- /dev/null +++ b/db/routines/vn/functions/travel_hasUniqueAwb.sql @@ -0,0 +1,28 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`travel_hasUniqueAwb`( + vSelf INT +) + RETURNS BOOL + READS SQL DATA +BEGIN +/** + * Comprueba que el travel pasado tiene un AWB lógico, + * no se pueden tener varios AWB asociados al mismo DUA + * + * @param vSelf Id del travel + */ + DECLARE vHasUniqueAwb BOOL DEFAULT TRUE; + + SELECT NOT COUNT(t2.awbFk) INTO vHasUniqueAwb + FROM entry e + JOIN travel t ON t.id = e.travelFk + JOIN duaEntry de ON de.entryFk = e.id + JOIN duaEntry de2 ON de2.duaFk = de.duaFk + JOIN entry e2 ON e2.id = de2.entryFk + JOIN travel t2 ON t2.id = e2.travelFk + WHERE t.id = vSelf + AND t2.awbFk <> t.awbFk; + + RETURN vHasUniqueAwb; +END$$ +DELIMITER ; diff --git a/db/routines/vn/procedures/zone_getCollisions.sql b/db/routines/vn/procedures/zone_getCollisions.sql index f6779e1b78..023b9aac27 100644 --- a/db/routines/vn/procedures/zone_getCollisions.sql +++ b/db/routines/vn/procedures/zone_getCollisions.sql @@ -1,8 +1,9 @@ DELIMITER $$ + CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getCollisions`() BEGIN /** - * Calcula si para un mismo codigo postal y dia + * Calcula si para un mismo codigo postal y dia * hay mas de una zona configurada y manda correo * */ @@ -10,17 +11,18 @@ BEGIN DECLARE vZoneFk INT; DECLARE vIsDone INT DEFAULT FALSE; DECLARE vTableCollisions TEXT; + DECLARE json_data JSON; DECLARE cur1 CURSOR FOR SELECT zoneFk from tmp.zoneOption; - + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vIsDone = TRUE; DROP TEMPORARY TABLE IF EXISTS tmp.zone; CREATE TEMPORARY TABLE tmp.zone - SELECT z.id + SELECT z.id FROM zone z JOIN agencyMode am ON am.id = z.agencyModeFk JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk - WHERE dm.code IN ('AGENCY','DELIVERY'); + WHERE dm.code IN ('AGENCY','DELIVERY'); CALL zone_getOptionsForShipment(util.VN_CURDATE(),FALSE); @@ -35,7 +37,7 @@ BEGIN PRIMARY KEY zoneFkk (zoneFk, geoFk), INDEX(geoFk)) ENGINE = MyISAM; - + OPEN cur1; cur1Loop: LOOP SET vIsDone = FALSE; @@ -43,82 +45,63 @@ BEGIN IF vIsDone THEN LEAVE cur1Loop; END IF; - + CALL zone_getLeaves(vZoneFk, NULL, NULL, TRUE); - myLoop: LOOP + myLoop: LOOP SET vGeoFk = NULL; - SELECT geoFk INTO vGeoFk + SELECT geoFk INTO vGeoFk FROM tmp.zoneNodes zn WHERE NOT isChecked LIMIT 1; - + IF vGeoFk IS NULL THEN LEAVE myLoop; END IF; - + CALL zone_getLeaves(vZoneFk, vGeoFk, NULL, TRUE); UPDATE tmp.zoneNodes - SET isChecked = TRUE + SET isChecked = TRUE WHERE geoFk = vGeoFk; END LOOP; END LOOP; CLOSE cur1; - DELETE FROM tmp.zoneNodes + DELETE FROM tmp.zoneNodes WHERE sons > 0; - + DROP TEMPORARY TABLE IF EXISTS geoCollision; CREATE TEMPORARY TABLE geoCollision SELECT z.agencyModeFk, zn.geoFk, zw.warehouseFk FROM tmp.zoneNodes zn JOIN zone z ON z.id = zn.zoneFk - JOIN zoneWarehouse zw ON z.id = zw.zoneFk + JOIN zoneWarehouse zw ON z.id = zw.zoneFk GROUP BY z.agencyModeFk, zn.geoFk, zw.warehouseFk HAVING count(*) > 1; - - SELECT ' - - - - - - - - ' INTO vTableCollisions; - - INSERT INTO mail (receiver,replyTo,subject,body) - SELECT 'pepe@verdnatura.es' receiver, - 'noreply@verdnatura.es' replyTo, - CONCAT('Colisiones en zonas ', util.VN_CURDATE()) subject, - CONCAT(vTableCollisions, - GROUP_CONCAT(sub.td SEPARATOR ''), - '
C.PostalNúmero de zonaPrecioZonaAlmacénSalix
') body - FROM(SELECT - CONCAT(' - ', zn.name, ' - ', zoneFk,' - ', z.price,' - ', z.name,' - ', w.name, ' - ', CONCAT('' - 'https://salix.verdnatura.es/#!/zone/', - zoneFk, - '/location?q=%7B%22search%22:%22', - zn.name, - '%22%7D'),' - ') td - FROM tmp.zoneNodes zn - JOIN zone z ON z.id = zn.zoneFk - JOIN geoCollision gc ON gc.agencyModeFk = z.agencyModeFk AND zn.geoFk = gc.geoFk - JOIN warehouse w ON w.id = gc.warehouseFk) sub; - - DROP TEMPORARY TABLE - geoCollision, + + -- Recojo los datos de la zona que ha dado conflicto + SELECT JSON_ARRAYAGG( + JSON_OBJECT( + 'zoneFk', zoneFk, + 'zn', JSON_OBJECT('name', zn.name), + 'z', JSON_OBJECT('name', z.name,'price', z.price), + 'w', JSON_OBJECT('name', w.name) + ) + ) FROM tmp.zoneNodes zn + JOIN zone z ON z.id = zn.zoneFk + JOIN geoCollision gc ON gc.agencyModeFk = z.agencyModeFk AND zn.geoFk = gc.geoFk + JOIN warehouse w ON w.id = gc.warehouseFk + INTO json_data; + + -- Creo un registro de la notificacion 'zone-included' para reportar via email + SELECT util.notification_send( + 'zone-included', + JSON_OBJECT('zoneCollisions',json_data), + account.myUser_getId() + ); + + DROP TEMPORARY TABLE + geoCollision, tmp.zone, tmp.zoneNodes; END$$ diff --git a/db/routines/vn/triggers/entry_beforeInsert.sql b/db/routines/vn/triggers/entry_beforeInsert.sql index f475630dbc..c0c0aa28cc 100644 --- a/db/routines/vn/triggers/entry_beforeInsert.sql +++ b/db/routines/vn/triggers/entry_beforeInsert.sql @@ -7,6 +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'); + END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/entry_beforeUpdate.sql b/db/routines/vn/triggers/entry_beforeUpdate.sql index 60b83002cc..384feb4580 100644 --- a/db/routines/vn/triggers/entry_beforeUpdate.sql +++ b/db/routines/vn/triggers/entry_beforeUpdate.sql @@ -8,13 +8,18 @@ BEGIN DECLARE vHasDistinctWarehouses BOOL; SET NEW.editorFk = account.myUser_getId(); + + IF NOT (NEW.travelFk <=> OLD.travelFk) THEN - IF !(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'); + END IF; + SELECT COUNT(*) > 0 INTO vIsVirtual FROM entryVirtual WHERE entryFk = NEW.id; - SELECT !(o.warehouseInFk <=> n.warehouseInFk) - OR !(o.warehouseOutFk <=> n.warehouseOutFk) + SELECT NOT (o.warehouseInFk <=> n.warehouseInFk) + OR NOT (o.warehouseOutFk <=> n.warehouseOutFk) INTO vHasDistinctWarehouses FROM travel o, travel n WHERE o.id = OLD.travelFk @@ -43,9 +48,8 @@ BEGIN SET NEW.currencyFk = entry_getCurrency(NEW.currencyFk, NEW.supplierFk); END IF; - IF NOT (NEW.travelFk <=> OLD.travelFk) - OR NOT (NEW.currencyFk <=> OLD.currencyFk) THEN - SET NEW.commission = entry_getCommission(NEW.travelFk, NEW.currencyFk,NEW.supplierFk); + IF NOT (NEW.travelFk <=> OLD.travelFk) OR NOT (NEW.currencyFk <=> OLD.currencyFk) THEN + SET NEW.commission = entry_getCommission(NEW.travelFk, NEW.currencyFk, NEW.supplierFk); END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/travel_afterUpdate.sql b/db/routines/vn/triggers/travel_afterUpdate.sql index b4e40ae41d..7752505e38 100644 --- a/db/routines/vn/triggers/travel_afterUpdate.sql +++ b/db/routines/vn/triggers/travel_afterUpdate.sql @@ -5,7 +5,7 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`travel_afterUpdate` BEGIN CALL stock.log_add('travel', NEW.id, OLD.id); - IF !(NEW.shipped <=> OLD.shipped) THEN + IF NOT(NEW.shipped <=> OLD.shipped) THEN UPDATE entry SET commission = entry_getCommission(travelFk, currencyFk,supplierFk) WHERE travelFk = NEW.id; @@ -23,5 +23,9 @@ BEGIN CALL buy_checkItem(); 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'); + END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/travel_beforeInsert.sql b/db/routines/vn/triggers/travel_beforeInsert.sql index 4e1dae3ef0..817bd69bb1 100644 --- a/db/routines/vn/triggers/travel_beforeInsert.sql +++ b/db/routines/vn/triggers/travel_beforeInsert.sql @@ -8,5 +8,9 @@ BEGIN CALL travel_checkDates(NEW.shipped, NEW.landed); 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'); + END IF; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/zoneIncluded_afterDelete.sql b/db/routines/vn/triggers/zoneIncluded_afterDelete.sql index 6d184bb127..18332bb552 100644 --- a/db/routines/vn/triggers/zoneIncluded_afterDelete.sql +++ b/db/routines/vn/triggers/zoneIncluded_afterDelete.sql @@ -8,5 +8,6 @@ BEGIN `changedModel` = 'zoneIncluded', `changedModelId` = OLD.zoneFk, `userFk` = account.myUser_getId(); + END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/zoneIncluded_beforeInsert.sql b/db/routines/vn/triggers/zoneIncluded_beforeInsert.sql index 5eff33efa2..18895c9a51 100644 --- a/db/routines/vn/triggers/zoneIncluded_beforeInsert.sql +++ b/db/routines/vn/triggers/zoneIncluded_beforeInsert.sql @@ -4,5 +4,6 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`zoneIncluded_beforeIn FOR EACH ROW BEGIN SET NEW.editorFk = account.myUser_getId(); + END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/zoneIncluded_beforeUpdate.sql b/db/routines/vn/triggers/zoneIncluded_beforeUpdate.sql index 445f37699f..e3f0a27e21 100644 --- a/db/routines/vn/triggers/zoneIncluded_beforeUpdate.sql +++ b/db/routines/vn/triggers/zoneIncluded_beforeUpdate.sql @@ -4,5 +4,6 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`zoneIncluded_beforeUp FOR EACH ROW BEGIN SET NEW.editorFk = account.myUser_getId(); + END$$ DELIMITER ; diff --git a/db/versions/10881-greenHydrangea/00-alterTableNotification.sql b/db/versions/10881-greenHydrangea/00-alterTableNotification.sql new file mode 100644 index 0000000000..068d77839f --- /dev/null +++ b/db/versions/10881-greenHydrangea/00-alterTableNotification.sql @@ -0,0 +1 @@ +ALTER TABLE util.notification MODIFY COLUMN id int(11) auto_increment NOT NULL; diff --git a/db/versions/10881-greenHydrangea/01-notification.vn.sql b/db/versions/10881-greenHydrangea/01-notification.vn.sql new file mode 100644 index 0000000000..ab54805485 --- /dev/null +++ b/db/versions/10881-greenHydrangea/01-notification.vn.sql @@ -0,0 +1,15 @@ +INSERT IGNORE INTO util.notification ( `name`,`description`) + VALUES + ( 'zone-included','An email to notify zoneCollisions'); + +-- Change value if destionation user should be different +SET @DESTINATION_USER = "pepe"; + +SET @MaxId = LAST_INSERT_ID(); + +INSERT IGNORE INTO util.notificationSubscription (notificationFk,userFk) + VALUES( + @MaxId, (SELECT id from `account`.`user` where name = @DESTINATION_USER)); + +INSERT IGNORE INTO util.notificationAcl (notificationFk,roleFk) + SELECT @MaxId, (SELECT role from `account`.`user` where name = @DESTINATION_USER) FROM util.notification WHERE name= "zone-included"; diff --git a/db/versions/10893-limeFern/00-sage.sql b/db/versions/10893-limeFern/00-sage.sql new file mode 100644 index 0000000000..d4c7e62215 --- /dev/null +++ b/db/versions/10893-limeFern/00-sage.sql @@ -0,0 +1,73 @@ +-- Auto-generated SQL script #202403061303 +UPDATE vn.company + SET companyCode=0 + WHERE id=69; +UPDATE vn.company + SET companyCode=1 + WHERE id=442; +UPDATE vn.company + SET companyCode=4 + WHERE id=567; +UPDATE vn.company + SET companyCode=2 + WHERE id=791; +UPDATE vn.company + SET companyCode=3 + WHERE id=792; +UPDATE vn.company + SET companyCode=5 + WHERE id=965; +UPDATE vn.company + SET companyCode=7 + WHERE id=1381; +UPDATE vn.company + SET companyCode=3 + WHERE id=1463; +UPDATE vn.company + SET companyCode=8 + WHERE id=2142; +UPDATE vn.company + SET companyCode=6 + WHERE id=2393; +UPDATE vn.company + SET companyCode=9 + WHERE id=3869; + +-- Auto-generated SQL script #202403061311 +UPDATE vn.company + SET sage200Company=NULL + WHERE id=69; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=442; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=567; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=791; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=792; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=965; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=1381; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=1463; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=2142; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=2393; +UPDATE vn.company + SET sage200Company=NULL + WHERE id=3869; + + +ALTER TABLE vn.company CHANGE sage200Company sage200Company__ int(2) DEFAULT NULL NULL COMMENT '@deprecated 06/03/2024'; +ALTER TABLE vn.company MODIFY COLUMN sage200Company__ int(2) DEFAULT NULL NULL COMMENT '@deprecated 06/03/2024'; diff --git a/db/versions/10919-brownMoss/00-firstScript.sql b/db/versions/10919-brownMoss/00-firstScript.sql new file mode 100644 index 0000000000..640d2180a1 --- /dev/null +++ b/db/versions/10919-brownMoss/00-firstScript.sql @@ -0,0 +1,3 @@ +-- Place your SQL code here + + diff --git a/front/core/services/auth.js b/front/core/services/auth.js index 844a5145d8..753bc3fba4 100644 --- a/front/core/services/auth.js +++ b/front/core/services/auth.js @@ -83,22 +83,27 @@ export default class Auth { } onLoginOk(json, now, remember) { - this.vnToken.set(json.data.token, now, json.data.ttl, remember); - - return this.loadAcls().then(() => { - let continueHash = this.$state.params.continue; - if (continueHash) - this.$window.location = continueHash; - else - this.$state.go('home'); - }); + return this.$http.get('VnUsers/ShareToken', { + 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'); + }); + }).catch(() => {}); } logout() { + this.$http.post('Accounts/logout', null, {headers: {'Authorization': this.vnToken.tokenMultimedia}, + }).catch(() => {}); + let promise = this.$http.post('VnUsers/logout', null, { headers: {Authorization: this.vnToken.token} }).catch(() => {}); - this.vnToken.unset(); this.loggedIn = false; this.vnModules.reset(); diff --git a/front/core/services/interceptor.js b/front/core/services/interceptor.js index 0c3253c693..90d813ed43 100644 --- a/front/core/services/interceptor.js +++ b/front/core/services/interceptor.js @@ -19,7 +19,7 @@ function interceptor($q, vnApp, $translate) { if (config.url.charAt(0) !== '/' && apiPath) config.url = `${apiPath}${config.url}`; - if (token) + if (token && !config.headers.Authorization) config.headers.Authorization = token; if ($translate.use()) config.headers['Accept-Language'] = $translate.use(); diff --git a/front/core/services/token.js b/front/core/services/token.js index c8cb4f6bb6..125de6b9aa 100644 --- a/front/core/services/token.js +++ b/front/core/services/token.js @@ -24,21 +24,22 @@ export default class Token { } catch (e) {} } - set(token, created, ttl, remember) { + set(token, tokenMultimedia, created, ttl, remember) { this.unset(); Object.assign(this, { token, + tokenMultimedia, created, ttl, remember }); - this.vnInterceptor.setToken(token); + this.vnInterceptor.setToken(token, tokenMultimedia); try { if (remember) - this.setStorage(localStorage, token, created, ttl); + this.setStorage(localStorage, token, tokenMultimedia, created, ttl); else - this.setStorage(sessionStorage, token, created, ttl); + this.setStorage(sessionStorage, token, tokenMultimedia, created, ttl); } catch (err) { console.error(err); } @@ -46,6 +47,7 @@ export default class Token { unset() { this.token = null; + this.tokenMultimedia = null; this.created = null; this.ttl = null; this.remember = null; @@ -57,13 +59,15 @@ export default class Token { getStorage(storage) { this.token = storage.getItem('vnToken'); + this.tokenMultimedia = storage.getItem('vnTokenMultimedia'); if (!this.token) return; const created = storage.getItem('vnTokenCreated'); this.created = created && new Date(created); this.ttl = storage.getItem('vnTokenTtl'); } - setStorage(storage, token, created, ttl) { + setStorage(storage, token, tokenMultimedia, created, ttl) { + storage.setItem('vnTokenMultimedia', tokenMultimedia); storage.setItem('vnToken', token); storage.setItem('vnTokenCreated', created.toJSON()); storage.setItem('vnTokenTtl', ttl); @@ -71,6 +75,7 @@ export default class Token { removeStorage(storage) { storage.removeItem('vnToken'); + storage.removeItem('vnTokenMultimedia'); storage.removeItem('vnTokenCreated'); storage.removeItem('vnTokenTtl'); } diff --git a/front/salix/components/layout/index.js b/front/salix/components/layout/index.js index 89912d4e39..e935c6d99f 100644 --- a/front/salix/components/layout/index.js +++ b/front/salix/components/layout/index.js @@ -23,8 +23,7 @@ export class Layout extends Component { if (!this.$.$root.user) return; const userId = this.$.$root.user.id; - const token = this.vnToken.token; - return `/api/Images/user/160x160/${userId}/download?access_token=${token}`; + return `/api/Images/user/160x160/${userId}/download?access_token=${this.vnToken.tokenMultimedia}`; } refresh() { diff --git a/front/salix/components/log/index.html b/front/salix/components/log/index.html index c750301001..a3aaf00113 100644 --- a/front/salix/components/log/index.html +++ b/front/salix/components/log/index.html @@ -31,7 +31,7 @@ ng-click="$ctrl.showDescriptor($event, userLog)"> + ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}"> @@ -181,7 +181,7 @@ val="{{::nickname}}"> + ng-src="/api/Images/user/160x160/{{::id}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}">
diff --git a/front/salix/module.js b/front/salix/module.js index 0ce855308f..53b7184275 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -13,7 +13,7 @@ export function run($window, $rootScope, vnAuth, vnApp, vnToken, $state) { if (!collection || !size || !id) return; const basePath = `/api/Images/${collection}/${size}/${id}`; - return `${basePath}/download?access_token=${vnToken.token}`; + return `${basePath}/download?access_token=${vnToken.tokenMultimedia}`; }; $window.validations = {}; diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 53b1a8bb5b..31b954a32b 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -220,5 +220,7 @@ "Shelving not valid": "Shelving not valid", "printerNotExists": "The printer does not exist", "There are not picking tickets": "There are not picking tickets", - "ticketCommercial": "The ticket {{ ticket }} for the salesperson {{ salesMan }} is in preparation. (automatically generated message)" + "ticketCommercial": "The ticket {{ ticket }} for the salesperson {{ salesMan }} is in preparation. (automatically generated message)", + "This password can only be changed by the user themselves": "This password can only be changed by the user themselves", + "They're not your subordinate": "They're not your subordinate" } diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 3748b6eafc..7730d4a8cf 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -346,5 +346,7 @@ "CountryFK cannot be empty": "El país no puede estar vacío", "Cmr file does not exist": "El archivo del cmr no existe", "You are not allowed to modify the alias": "No estás autorizado a modificar el alias", - "The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas" -} \ No newline at end of file + "The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas", + "This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario", + "They're not your subordinate": "No es tu subordinado/a." +} diff --git a/modules/account/back/methods/account/logout.js b/modules/account/back/methods/account/logout.js index 5db3efa339..7d2e8153e3 100644 --- a/modules/account/back/methods/account/logout.js +++ b/modules/account/back/methods/account/logout.js @@ -15,7 +15,8 @@ module.exports = Self => { http: { path: `/logout`, verb: 'POST' - } + }, + accessScopes: ['DEFAULT', 'read:multimedia'] }); Self.logout = async ctx => Self.app.models.VnUser.logout(ctx.req.accessToken.id); diff --git a/modules/account/back/models/account.js b/modules/account/back/models/account.js index 5021a5d948..ceb26053c6 100644 --- a/modules/account/back/models/account.js +++ b/modules/account/back/models/account.js @@ -1,4 +1,7 @@ +const ForbiddenError = require('vn-loopback/util/forbiddenError'); +const {models} = require('vn-loopback/server/server'); + module.exports = Self => { require('../methods/account/sync')(Self); require('../methods/account/sync-by-id')(Self); @@ -7,4 +10,11 @@ module.exports = Self => { require('../methods/account/logout')(Self); require('../methods/account/change-password')(Self); require('../methods/account/set-password')(Self); + + Self.setUnverifiedPassword = async(id, pass, options) => { + const {emailVerified} = await models.VnUser.findById(id, {fields: ['emailVerified']}, options); + if (emailVerified) throw new ForbiddenError('This password can only be changed by the user themselves'); + + await models.VnUser.setPassword(id, pass, options); + }; }; diff --git a/modules/claim/back/methods/claim/downloadFile.js b/modules/claim/back/methods/claim/downloadFile.js index 750356b0b1..61784f39e7 100644 --- a/modules/claim/back/methods/claim/downloadFile.js +++ b/modules/claim/back/methods/claim/downloadFile.js @@ -32,7 +32,8 @@ module.exports = Self => { http: { path: `/:id/downloadFile`, verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.downloadFile = async function(ctx, id) { diff --git a/modules/client/front/balance/index/index.html b/modules/client/front/balance/index/index.html index faf772c2d6..34524d2f39 100644 --- a/modules/client/front/balance/index/index.html +++ b/modules/client/front/balance/index/index.html @@ -114,7 +114,7 @@ + href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}"> diff --git a/modules/invoiceOut/back/methods/invoiceOut/download.js b/modules/invoiceOut/back/methods/invoiceOut/download.js index 4c76f7c072..cb71121d5d 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/download.js +++ b/modules/invoiceOut/back/methods/invoiceOut/download.js @@ -31,7 +31,8 @@ module.exports = Self => { http: { path: '/:id/download', verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.download = async function(ctx, id, options) { diff --git a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js index fe005f1abe..4f2a8aab3a 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js +++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js @@ -31,7 +31,8 @@ module.exports = Self => { http: { path: '/downloadZip', verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.downloadZip = async function(ctx, ids, options) { diff --git a/modules/invoiceOut/front/descriptor-menu/index.html b/modules/invoiceOut/front/descriptor-menu/index.html index face462939..1bf34831e4 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.html +++ b/modules/invoiceOut/front/descriptor-menu/index.html @@ -37,7 +37,7 @@ diff --git a/modules/invoiceOut/front/index/index.js b/modules/invoiceOut/front/index/index.js index 2cde3c940d..403c51d582 100644 --- a/modules/invoiceOut/front/index/index.js +++ b/modules/invoiceOut/front/index/index.js @@ -25,7 +25,7 @@ export default class Controller extends Section { openPdf() { if (this.checked.length <= 1) { const [invoiceOutId] = this.checked; - const url = `api/InvoiceOuts/${invoiceOutId}/download?access_token=${this.vnToken.token}`; + const url = `api/InvoiceOuts/${invoiceOutId}/download?access_token=${this.vnToken.tokenMultimedia}`; window.open(url, '_blank'); } else { const invoiceOutIds = this.checked; diff --git a/modules/item/back/methods/item-image-queue/download.js b/modules/item/back/methods/item-image-queue/download.js index eb952daa4b..e1bc248ae9 100644 --- a/modules/item/back/methods/item-image-queue/download.js +++ b/modules/item/back/methods/item-image-queue/download.js @@ -11,6 +11,7 @@ module.exports = Self => { path: `/download`, verb: 'POST', }, + accessScopes: ['read:multimedia'] }); Self.download = async() => { diff --git a/modules/route/back/methods/route/downloadCmrsZip.js b/modules/route/back/methods/route/downloadCmrsZip.js index 58445f6f1b..43f6e9648f 100644 --- a/modules/route/back/methods/route/downloadCmrsZip.js +++ b/modules/route/back/methods/route/downloadCmrsZip.js @@ -29,7 +29,8 @@ module.exports = Self => { http: { path: '/downloadCmrsZip', verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.downloadCmrsZip = async function(ctx, ids, options) { diff --git a/modules/route/back/methods/route/downloadZip.js b/modules/route/back/methods/route/downloadZip.js index 597f1d1f6b..d7fc30aa38 100644 --- a/modules/route/back/methods/route/downloadZip.js +++ b/modules/route/back/methods/route/downloadZip.js @@ -29,7 +29,8 @@ module.exports = Self => { http: { path: '/downloadZip', verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.downloadZip = async function(ctx, id, options) { diff --git a/modules/route/back/methods/route/driverRoutePdf.js b/modules/route/back/methods/route/driverRoutePdf.js index f0cd75f0ef..e7b4dee176 100644 --- a/modules/route/back/methods/route/driverRoutePdf.js +++ b/modules/route/back/methods/route/driverRoutePdf.js @@ -34,7 +34,9 @@ module.exports = Self => { http: { path: '/:id/driver-route-pdf', verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] + }); Self.driverRoutePdf = (ctx, id) => Self.printReport(ctx, id, 'driver-route'); diff --git a/modules/route/front/index/index.js b/modules/route/front/index/index.js index 7c19a26cdf..0c5dfe7f3f 100644 --- a/modules/route/front/index/index.js +++ b/modules/route/front/index/index.js @@ -40,7 +40,7 @@ export default class Controller extends Section { const stringRoutesIds = routesIds.join(','); if (this.checked.length <= 1) { - const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${this.vnToken.token}`; + const url = `api/Routes/${stringRoutesIds}/driver-route-pdf?access_token=${this.vnToken.tokenMultimedia}`; window.open(url, '_blank'); } else { const serializedParams = this.$httpParamSerializer({ diff --git a/modules/worker/back/methods/worker-dms/downloadFile.js b/modules/worker/back/methods/worker-dms/downloadFile.js index cc8653e0e5..08fbcf9246 100644 --- a/modules/worker/back/methods/worker-dms/downloadFile.js +++ b/modules/worker/back/methods/worker-dms/downloadFile.js @@ -29,7 +29,8 @@ module.exports = Self => { http: { path: `/:id/downloadFile`, verb: 'GET' - } + }, + accessScopes: ['read:multimedia'] }); Self.downloadFile = async function(ctx, id) { diff --git a/modules/worker/back/methods/worker/setPassword.js b/modules/worker/back/methods/worker/setPassword.js index 43d3d946f5..9969530a4f 100644 --- a/modules/worker/back/methods/worker/setPassword.js +++ b/modules/worker/back/methods/worker/setPassword.js @@ -1,31 +1,29 @@ -const UserError = require('vn-loopback/util/user-error'); +const ForbiddenError = require('vn-loopback/util/forbiddenError'); module.exports = Self => { Self.remoteMethodCtx('setPassword', { description: 'Set a new password', - accepts: [ - { - arg: 'workerFk', - type: 'number', - required: true, - description: 'The worker id', - }, - { - arg: 'newPass', - type: 'String', - required: true, - description: 'The new worker password' - } - ], + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The worker id', + http: {source: 'path'} + }, { + arg: 'newPass', + type: 'String', + required: true, + description: 'The new worker password' + }], http: { path: `/:id/setPassword`, verb: 'PATCH' } }); - Self.setPassword = async(ctx, options) => { + Self.setPassword = async(ctx, id, newPass, options) => { const models = Self.app.models; const myOptions = {}; - const {args} = ctx; let tx; + if (typeof options == 'object') Object.assign(myOptions, options); if (!myOptions.transaction) { @@ -33,11 +31,10 @@ module.exports = Self => { myOptions.transaction = tx; } try { - const isSubordinate = await models.Worker.isSubordinate(ctx, args.workerFk, myOptions); - if (!isSubordinate) throw new UserError('You don\'t have enough privileges.'); + const isSubordinate = await Self.isSubordinate(ctx, id, myOptions); + if (!isSubordinate) throw new ForbiddenError('They\'re not your subordinate'); - await models.VnUser.setPassword(args.workerFk, args.newPass, myOptions); - await models.VnUser.updateAll({id: args.workerFk}, {emailVerified: true}, myOptions); + await models.Account.setUnverifiedPassword(id, newPass, myOptions); if (tx) await tx.commit(); } catch (e) { diff --git a/modules/worker/back/methods/worker/specs/setPassword.spec.js b/modules/worker/back/methods/worker/specs/setPassword.spec.js index fbb403b24c..8d152bdd15 100644 --- a/modules/worker/back/methods/worker/specs/setPassword.spec.js +++ b/modules/worker/back/methods/worker/specs/setPassword.spec.js @@ -1,31 +1,30 @@ -const UserError = require('vn-loopback/util/user-error'); - -const models = require('vn-loopback/server/server').models; +const {models} = require('vn-loopback/server/server'); describe('worker setPassword()', () => { let ctx; + const newPass = 'H3rn4d3z#'; + const employeeId = 1; + const managerId = 20; + const administrativeId = 5; + beforeAll(() => { ctx = { req: { - accessToken: {}, + accessToken: {userId: managerId}, headers: {origin: 'http://localhost'} }, - args: {workerFk: 9} }; }); - beforeEach(() => { - ctx.req.accessToken.userId = 20; - ctx.args.newPass = 'H3rn4d3z#'; - }); - - it('should change the password', async() => { + it('should change the password if it is a subordinate and the email is not verified', async() => { const tx = await models.Worker.beginTransaction({}); try { const options = {transaction: tx}; - await models.Worker.setPassword(ctx, options); + await models.Worker.setPassword(ctx, employeeId, newPass, options); + const isNewPass = await passHasBeenChanged(employeeId, newPass, options); + expect(isNewPass).toBeTrue(); await tx.rollback(); } catch (e) { await tx.rollback(); @@ -33,29 +32,48 @@ describe('worker setPassword()', () => { } }); - it('should throw an error: Password does not meet requirements', async() => { - const tx = await models.Collection.beginTransaction({}); - ctx.args.newPass = 'Hi'; + it('should not change the password if it is a subordinate and the email is verified', async() => { + const tx = await models.Worker.beginTransaction({}); + try { const options = {transaction: tx}; - await models.Worker.setPassword(ctx, options); + await models.VnUser.updateAll({id: employeeId}, {emailVerified: true}, options); + await models.Worker.setPassword(ctx, employeeId, newPass, options); + + await tx.rollback(); + } catch (e) { + expect(e.message).toEqual(`This password can only be changed by the user themselves`); + await tx.rollback(); + } + }); + + it('should not change the password if it is not a subordinate', async() => { + const tx = await models.Worker.beginTransaction({}); + try { + const options = {transaction: tx}; + await models.Worker.setPassword(ctx, administrativeId, newPass, options); + await tx.rollback(); + } catch (e) { + expect(e.message).toEqual(`They're not your subordinate`); + await tx.rollback(); + } + }); + + it('should throw an error: Password does not meet requirements', async() => { + const tx = await models.Worker.beginTransaction({}); + const newPass = 'Hi'; + try { + const options = {transaction: tx}; + await models.Worker.setPassword(ctx, employeeId, newPass, options); await tx.rollback(); } catch (e) { expect(e.sqlMessage).toEqual('Password does not meet requirements'); await tx.rollback(); } }); - - it('should throw an error: You don\'t have enough privileges.', async() => { - ctx.req.accessToken.userId = 5; - const tx = await models.Collection.beginTransaction({}); - try { - const options = {transaction: tx}; - await models.Worker.setPassword(ctx, options); - await tx.rollback(); - } catch (e) { - expect(e).toEqual(new UserError(`You don't have enough privileges.`)); - await tx.rollback(); - } - }); }); + +const passHasBeenChanged = async(userId, pass, options) => { + const user = await models.VnUser.findById(userId, null, options); + return user.hasPassword(pass); +}; diff --git a/modules/worker/front/descriptor/index.html b/modules/worker/front/descriptor/index.html index 8290e2a155..73332efac3 100644 --- a/modules/worker/front/descriptor/index.html +++ b/modules/worker/front/descriptor/index.html @@ -11,8 +11,8 @@ ? 'Click to allow the user to be disabled' : 'Click to exclude the user from getting disabled'}} - - Change password + + Change password diff --git a/modules/worker/front/descriptor/index.js b/modules/worker/front/descriptor/index.js index 13ffa6f2f0..d7962369c6 100644 --- a/modules/worker/front/descriptor/index.js +++ b/modules/worker/front/descriptor/index.js @@ -69,6 +69,7 @@ class Controller extends Descriptor { } ] }; + return this.getData(`Workers/${this.id}`, {filter}) .then(res => this.entity = res.data); } @@ -86,15 +87,14 @@ class Controller extends Descriptor { if (this.newPassword != this.repeatPassword) throw new UserError(`Passwords don't match`); this.$http.patch( - `Workers/${this.entity.id}/setPassword`, - {workerFk: this.entity.id, newPass: this.newPassword} + `Workers/${this.entity.id}/setPassword`, {newPass: this.newPassword} ) .then(() => { this.vnApp.showSuccess(this.$translate.instant('Password changed!')); - }); + }).then(() => this.loadData()); } } -Controller.$inject = ['$element', '$scope', '$rootScope']; +Controller.$inject = ['$element', '$scope', '$rootScope', 'vnConfig']; ngModule.vnComponent('vnWorkerDescriptor', { template: require('./index.html'), diff --git a/modules/worker/front/descriptor/index.spec.js b/modules/worker/front/descriptor/index.spec.js index d158a9e8e1..4f7fa6a05a 100644 --- a/modules/worker/front/descriptor/index.spec.js +++ b/modules/worker/front/descriptor/index.spec.js @@ -16,6 +16,7 @@ describe('vnWorkerDescriptor', () => { const id = 1; const response = 'foo'; + $httpBackend.whenGET('UserConfigs/getUserConfig').respond({}); $httpBackend.expectRoute('GET', `Workers/${id}`).respond(response); controller.id = id; $httpBackend.flush(); diff --git a/modules/zone/back/methods/zone/specs/toggleIsIncluded.spec.js b/modules/zone/back/methods/zone/specs/toggleIsIncluded.spec.js index 746a2d0bd9..2da1763307 100644 --- a/modules/zone/back/methods/zone/specs/toggleIsIncluded.spec.js +++ b/modules/zone/back/methods/zone/specs/toggleIsIncluded.spec.js @@ -1,6 +1,5 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); - describe('zone toggleIsIncluded()', () => { beforeAll(async() => { const activeCtx = { @@ -58,7 +57,7 @@ describe('zone toggleIsIncluded()', () => { await models.Zone.toggleIsIncluded(1, 20, false, options); - let result = await models.Zone.toggleIsIncluded(1, 20, undefined, options); + const result = await models.Zone.toggleIsIncluded(1, 20, undefined, options); expect(result).toEqual({count: 1}); diff --git a/modules/zone/back/model-config.json b/modules/zone/back/model-config.json index 261a89902a..3bbbe0d1b3 100644 --- a/modules/zone/back/model-config.json +++ b/modules/zone/back/model-config.json @@ -3,10 +3,10 @@ "dataSource": "vn" }, "AgencyMode": { - "dataSource": "vn" + "dataSource": "vn" }, "DeliveryMethod": { - "dataSource": "vn" + "dataSource": "vn" }, "Zone": { "dataSource": "vn" diff --git a/print/templates/email/zone-included/assets/css/import.js b/print/templates/email/zone-included/assets/css/import.js new file mode 100644 index 0000000000..4b4bb70869 --- /dev/null +++ b/print/templates/email/zone-included/assets/css/import.js @@ -0,0 +1,11 @@ +const Stylesheet = require(`vn-print/core/stylesheet`); + +const path = require('path'); +const vnPrintPath = path.resolve('print'); + +module.exports = new Stylesheet([ + `${vnPrintPath}/common/css/spacing.css`, + `${vnPrintPath}/common/css/misc.css`, + `${vnPrintPath}/common/css/layout.css`, + `${vnPrintPath}/common/css/email.css`]) + .mergeStyles(); diff --git a/print/templates/email/zone-included/locale/es.yml b/print/templates/email/zone-included/locale/es.yml new file mode 100644 index 0000000000..cb35a55ccd --- /dev/null +++ b/print/templates/email/zone-included/locale/es.yml @@ -0,0 +1,7 @@ +subject: Colisiones en zonas +title: "La zona {0} y localización {1} ha sido registrada en más de un sitio" +postalCode: C. Postal +zoneFk: Número de zona +price: Precio +zone: Zona +warehouse: Almacén diff --git a/print/templates/email/zone-included/sql/zoneIncluded.sql b/print/templates/email/zone-included/sql/zoneIncluded.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/print/templates/email/zone-included/zone-included.html b/print/templates/email/zone-included/zone-included.html new file mode 100644 index 0000000000..78915faada --- /dev/null +++ b/print/templates/email/zone-included/zone-included.html @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ $t('postalCode') }}{{ $t('zoneFk') }}{{ $t('price') }}{{ $t('zone') }}{{ $t('warehouse') }}
{{ zone.zn.name }}{{ zone.zoneFk }}{{ zone.z.price }}{{ zone.z.name }}{{ zone.w.name }} + + https://salix.verdnatura.es/#!/zone/ + {{zone.zoneFk}} + /location?q=%7B%22search%22:%22 + {{zone.zn.name}} + %22%7D
+ + diff --git a/print/templates/email/zone-included/zone-included.js b/print/templates/email/zone-included/zone-included.js new file mode 100755 index 0000000000..4de4777f3f --- /dev/null +++ b/print/templates/email/zone-included/zone-included.js @@ -0,0 +1,12 @@ +const Component = require(`vn-print/core/component`); +const emailHeader = new Component('email-header'); + +module.exports = { + name: 'zone-included', + components: { + 'email-header': emailHeader.build(), + }, + props: { + zoneCollisions: {type: Array, required: true} + } +};