diff --git a/back/methods/account/recover-password.js b/back/methods/account/recover-password.js new file mode 100644 index 000000000..ddea76829 --- /dev/null +++ b/back/methods/account/recover-password.js @@ -0,0 +1,30 @@ +module.exports = Self => { + Self.remoteMethod('recoverPassword', { + description: 'Send email to the user', + accepts: [ + { + arg: 'email', + type: 'string', + description: 'The email of user', + required: true + } + ], + http: { + path: `/recoverPassword`, + verb: 'POST' + } + }); + + Self.recoverPassword = async function(email) { + const models = Self.app.models; + + try { + await models.user.resetPassword({email, emailTemplate: 'recover-password'}); + } catch (err) { + if (err.code === 'EMAIL_NOT_FOUND') + return; + else + throw err; + } + }; +}; diff --git a/back/methods/account/specs/set-password.spec.js b/back/methods/account/specs/set-password.spec.js index c76fd52b8..fe71873de 100644 --- a/back/methods/account/specs/set-password.spec.js +++ b/back/methods/account/specs/set-password.spec.js @@ -1,6 +1,6 @@ const app = require('vn-loopback/server/server'); -describe('account changePassword()', () => { +describe('account setPassword()', () => { it('should throw an error when password does not meet requirements', async() => { let req = app.models.Account.setPassword(1, 'insecurePass'); diff --git a/back/models/account.js b/back/models/account.js index f74052b5c..c2502380a 100644 --- a/back/models/account.js +++ b/back/models/account.js @@ -1,4 +1,7 @@ +/* eslint max-len: ["error", { "code": 150 }]*/ const md5 = require('md5'); +const LoopBackContext = require('loopback-context'); +const {Email} = require('vn-print'); module.exports = Self => { require('../methods/account/login')(Self); @@ -6,6 +9,7 @@ module.exports = Self => { require('../methods/account/acl')(Self); require('../methods/account/change-password')(Self); require('../methods/account/set-password')(Self); + require('../methods/account/recover-password')(Self); require('../methods/account/validate-token')(Self); require('../methods/account/privileges')(Self); @@ -27,17 +31,62 @@ module.exports = Self => { ctx.data.password = md5(ctx.data.password); }); + Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => { + if (!ctx.args || !ctx.args.data.email) return; + const models = Self.app.models; + + const loopBackContext = LoopBackContext.getCurrentContext(); + const httpCtx = {req: loopBackContext.active}; + const httpRequest = httpCtx.req.http.req; + const headers = httpRequest.headers; + const origin = headers.origin; + const url = origin.split(':'); + + const userId = ctx.instance.id; + const user = await models.user.findById(userId); + + class Mailer { + async send(verifyOptions, cb) { + const params = { + url: verifyOptions.verifyHref, + recipient: verifyOptions.to, + lang: ctx.req.getLocale() + }; + + const email = new Email('email-verify', params); + email.send(); + + cb(null, verifyOptions.to); + } + } + + const options = { + type: 'email', + to: instance.email, + from: {}, + redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`, + template: false, + mailer: new Mailer, + host: url[1].split('/')[2], + port: url[2], + protocol: url[0], + user: Self + }; + + await user.verify(options); + }); + Self.remoteMethod('getCurrentUserData', { description: 'Gets the current user data', accepts: [ { arg: 'ctx', - type: 'Object', + type: 'object', http: {source: 'context'} } ], returns: { - type: 'Object', + type: 'object', root: true }, http: { @@ -58,7 +107,7 @@ module.exports = Self => { * * @param {Integer} userId The user id * @param {String} name The role name - * @param {Object} options Options + * @param {object} options Options * @return {Boolean} %true if user has the role, %false otherwise */ Self.hasRole = async function(userId, name, options) { @@ -70,8 +119,8 @@ module.exports = Self => { * Get all user roles. * * @param {Integer} userId The user id - * @param {Object} options Options - * @return {Object} User role list + * @param {object} options Options + * @return {object} User role list */ Self.getRoles = async(userId, options) => { let result = await Self.rawSql( diff --git a/back/models/account.json b/back/models/account.json index d0c17e70f..5e35c711a 100644 --- a/back/models/account.json +++ b/back/models/account.json @@ -40,6 +40,9 @@ "email": { "type": "string" }, + "emailVerified": { + "type": "boolean" + }, "created": { "type": "date" }, @@ -88,16 +91,23 @@ "principalType": "ROLE", "principalId": "$everyone", "permission": "ALLOW" - }, + }, + { + "property": "recoverPassword", + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + }, { - "property": "logout", + "property": "logout", "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW" }, { - "property": "validateToken", + "property": "validateToken", "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", diff --git a/back/models/specs/account.spec.js b/back/models/specs/account.spec.js index c52bc4378..f31c81b75 100644 --- a/back/models/specs/account.spec.js +++ b/back/models/specs/account.spec.js @@ -1,14 +1,14 @@ -const app = require('vn-loopback/server/server'); +const models = require('vn-loopback/server/server').models; describe('loopback model Account', () => { it('should return true if the user has the given role', async() => { - let result = await app.models.Account.hasRole(1, 'employee'); + let result = await models.Account.hasRole(1, 'employee'); expect(result).toBeTruthy(); }); it('should return false if the user doesnt have the given role', async() => { - let result = await app.models.Account.hasRole(1, 'administrator'); + let result = await models.Account.hasRole(1, 'administrator'); expect(result).toBeFalsy(); }); diff --git a/back/models/specs/user.spec.js b/back/models/specs/user.spec.js new file mode 100644 index 000000000..124afdc0c --- /dev/null +++ b/back/models/specs/user.spec.js @@ -0,0 +1,32 @@ +const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); + +describe('account recoverPassword()', () => { + const userId = 1107; + + const activeCtx = { + accessToken: {userId: userId}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + it('should send email with token', async() => { + const userId = 1107; + const user = await models.Account.findById(userId); + + await models.Account.recoverPassword(user.email); + + const result = await models.AccessToken.findOne({where: {userId: userId}}); + + expect(result).toBeDefined(); + }); +}); diff --git a/back/models/user.js b/back/models/user.js new file mode 100644 index 000000000..b24d702b3 --- /dev/null +++ b/back/models/user.js @@ -0,0 +1,27 @@ +const LoopBackContext = require('loopback-context'); +const {Email} = require('vn-print'); + +module.exports = function(Self) { + Self.on('resetPasswordRequest', async function(info) { + const loopBackContext = LoopBackContext.getCurrentContext(); + const httpCtx = {req: loopBackContext.active}; + const httpRequest = httpCtx.req.http.req; + const headers = httpRequest.headers; + const origin = headers.origin; + + const user = await Self.app.models.Account.findById(info.user.id); + const params = { + recipient: info.email, + lang: user.lang, + url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}` + }; + + const options = Object.assign({}, info.options); + for (const param in options) + params[param] = options[param]; + + const email = new Email(options.emailTemplate, params); + + return email.send(); + }); +}; diff --git a/db/changes/10470-family/00-accountingType.sql b/db/changes/10470-family/00-accountingType.sql deleted file mode 100644 index 964027e3a..000000000 --- a/db/changes/10470-family/00-accountingType.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE `vn`.`accountingType` ADD daysInFuture INT NULL; -ALTER TABLE `vn`.`accountingType` MODIFY COLUMN daysInFuture int(11) DEFAULT 0 NULL; -UPDATE `vn`.`accountingType` SET daysInFuture=1 WHERE id=8; \ No newline at end of file diff --git a/db/changes/10470-family/00-aclItemType.sql b/db/changes/10470-family/00-aclItemType.sql deleted file mode 100644 index 836a69dfd..000000000 --- a/db/changes/10470-family/00-aclItemType.sql +++ /dev/null @@ -1,4 +0,0 @@ -INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) - VALUES - ('ItemType', '*', 'READ', 'ALLOW', 'ROLE', 'employee'), - ('ItemType', '*', 'WRITE', 'ALLOW', 'ROLE', 'buyer'); \ No newline at end of file diff --git a/db/changes/10470-family/00-aclMdb.sql b/db/changes/10470-family/00-aclMdb.sql deleted file mode 100644 index b02ddc451..000000000 --- a/db/changes/10470-family/00-aclMdb.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE IF NOT EXISTS `vn`.`mdbBranch` ( - `name` VARCHAR(255), - PRIMARY KEY(`name`) -); - -CREATE TABLE IF NOT EXISTS `vn`.`mdbVersion` ( - `app` VARCHAR(255) NOT NULL, - `branchFk` VARCHAR(255) NOT NULL, - `version` INT, - CONSTRAINT `mdbVersion_branchFk` FOREIGN KEY (`branchFk`) REFERENCES `vn`.`mdbBranch` (`name`) ON DELETE CASCADE ON UPDATE CASCADE -); - -INSERT IGNORE INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) - VALUES(318, 'MdbVersion', '*', '*', 'ALLOW', 'ROLE', 'developer'); diff --git a/db/changes/10470-family/00-chat.sql b/db/changes/10470-family/00-chat.sql deleted file mode 100644 index d4a8f068a..000000000 --- a/db/changes/10470-family/00-chat.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE `vn`.`chat` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `senderFk` int(10) unsigned DEFAULT NULL, - `recipient` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL, - `dated` date DEFAULT NULL, - `checkUserStatus` tinyint(1) DEFAULT NULL, - `message` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL, - `status` tinyint(1) DEFAULT NULL, - `attempts` int(1) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `chat_FK` (`senderFk`), - CONSTRAINT `chat_FK` FOREIGN KEY (`senderFk`) REFERENCES `account`.`user` (`id`) ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; \ No newline at end of file diff --git a/db/changes/10470-family/00-creditInsurance.sql b/db/changes/10470-family/00-creditInsurance.sql deleted file mode 100644 index 9d4db470b..000000000 --- a/db/changes/10470-family/00-creditInsurance.sql +++ /dev/null @@ -1,8 +0,0 @@ -ALTER TABLE `vn`.`creditInsurance` ADD creditClassificationFk int(11) NULL; - -UPDATE `vn`.`creditInsurance` AS `destiny` - SET `destiny`.`creditClassificationFk`= (SELECT creditClassification FROM `vn`.`creditInsurance` AS `origin` WHERE `origin`.id = `destiny`.id); - -ALTER TABLE `vn`.`creditInsurance` - ADD CONSTRAINT `creditInsurance_creditClassificationFk` FOREIGN KEY (`creditClassificationFk`) - REFERENCES `vn`.`creditClassification` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/db/changes/10470-family/00-defaultViewConfig.sql b/db/changes/10470-family/00-defaultViewConfig.sql deleted file mode 100644 index d423599b1..000000000 --- a/db/changes/10470-family/00-defaultViewConfig.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO `salix`.`defaultViewConfig` (tableCode, columns) -VALUES ('clientsDetail', '{"id":true,"phone":true,"city":true,"socialName":true,"salesPersonFk":true,"email":true,"name":false,"fi":false,"credit":false,"creditInsurance":false,"mobile":false,"street":false,"countryFk":false,"provinceFk":false,"postcode":false,"created":false,"businessTypeFk":false,"payMethodFk":false,"sageTaxTypeFk":false,"sageTransactionTypeFk":false,"isActive":false,"isVies":false,"isTaxDataChecked":false,"isEqualizated":false,"isFreezed":false,"hasToInvoice":false,"hasToInvoiceByAddress":false,"isToBeMailed":false,"hasLcr":false,"hasCoreVnl":false,"hasSepaVnl":false}'); - diff --git a/db/changes/10470-family/00-ticket_doRefund.sql b/db/changes/10470-family/00-ticket_doRefund.sql deleted file mode 100644 index f4ecf29d7..000000000 --- a/db/changes/10470-family/00-ticket_doRefund.sql +++ /dev/null @@ -1,127 +0,0 @@ -DROP PROCEDURE IF EXISTS `vn`.`ticket_doRefund`; - -DELIMITER $$ -$$ -CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_doRefund`(OUT vNewTicket INT) -BEGIN -/** - * Crea un ticket de abono a partir de tmp.sale y/o tmp.ticketService - * - * @return vNewTicket - */ - DECLARE vDone BIT DEFAULT 0; - DECLARE vClientFk MEDIUMINT; - DECLARE vWarehouse TINYINT; - DECLARE vCompany MEDIUMINT; - DECLARE vAddress MEDIUMINT; - DECLARE vRefundAgencyMode INT; - DECLARE vItemFk INT; - DECLARE vQuantity DECIMAL (10,2); - DECLARE vConcept VARCHAR(50); - DECLARE vPrice DECIMAL (10,2); - DECLARE vDiscount TINYINT; - DECLARE vSaleNew INT; - DECLARE vSaleMain INT; - DECLARE vZoneFk INT; - DECLARE vDescription VARCHAR(50); - DECLARE vTaxClassFk INT; - DECLARE vTicketServiceTypeFk INT; - DECLARE vOriginTicket INT; - - DECLARE cSales CURSOR FOR - SELECT s.id, s.itemFk, - s.quantity, s.concept, s.price, s.discount - FROM tmp.sale s; - - DECLARE cTicketServices CURSOR FOR - SELECT ts.description, - ts.quantity, ts.price, ts.taxClassFk, ts.ticketServiceTypeFk - FROM tmp.ticketService ts; - - DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; - - SELECT sub.ticketFk INTO vOriginTicket - FROM ( - SELECT s.ticketFk - FROM tmp.sale s - UNION ALL - SELECT ts.ticketFk - FROM tmp.ticketService ts - ) sub - LIMIT 1; - - SELECT id INTO vRefundAgencyMode - FROM agencyMode WHERE `name` = 'ABONO'; - - SELECT clientFk, warehouseFk, companyFk, addressFk - INTO vClientFk, vWarehouse, vCompany, vAddress - FROM ticket - WHERE id = vOriginTicket; - - SELECT id INTO vZoneFk - FROM zone WHERE agencyModeFk = vRefundAgencyMode - LIMIT 1; - - INSERT INTO vn.ticket ( - clientFk, - shipped, - addressFk, - agencyModeFk, - nickname, - warehouseFk, - companyFk, - landed, - zoneFk - ) - SELECT - vClientFk, - CURDATE(), - vAddress, - vRefundAgencyMode, - a.nickname, - vWarehouse, - vCompany, - CURDATE(), - vZoneFk - FROM address a - WHERE a.id = vAddress; - - SET vNewTicket = LAST_INSERT_ID(); - - SET vDone := FALSE; - OPEN cSales; - FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount; - - WHILE NOT vDone DO - - INSERT INTO vn.sale(ticketFk, itemFk, quantity, concept, price, discount) - VALUES( vNewTicket, vItemFk, vQuantity, vConcept, vPrice, vDiscount ); - - SET vSaleNew = LAST_INSERT_ID(); - - INSERT INTO vn.saleComponent(saleFk,componentFk,`value`) - SELECT vSaleNew,componentFk,`value` - FROM vn.saleComponent - WHERE saleFk = vSaleMain; - - FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount; - - END WHILE; - CLOSE cSales; - - SET vDone := FALSE; - OPEN cTicketServices; - FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk; - - WHILE NOT vDone DO - - INSERT INTO vn.ticketService(description, quantity, price, taxClassFk, ticketFk, ticketServiceTypeFk) - VALUES(vDescription, vQuantity, vPrice, vTaxClassFk, vNewTicket, vTicketServiceTypeFk); - - FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk; - - END WHILE; - CLOSE cTicketServices; - - INSERT INTO vn.ticketRefund(refundTicketFk, originalTicketFk) - VALUES(vNewTicket, vOriginTicket); -END$$ -DELIMITER ; diff --git a/db/changes/10470-family/01-creditInsuranceTriggers.sql b/db/changes/10470-family/01-creditInsuranceTriggers.sql deleted file mode 100644 index 53ba3ba11..000000000 --- a/db/changes/10470-family/01-creditInsuranceTriggers.sql +++ /dev/null @@ -1,11 +0,0 @@ -DELIMITER $$ -$$ -CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`creditInsurance_beforeInsert` - BEFORE INSERT ON `creditInsurance` - FOR EACH ROW -BEGIN - IF NEW.creditClassificationFk THEN - SET NEW.creditClassification = NEW.creditClassificationFk; - END IF; -END$$ -DELIMITER ; \ No newline at end of file diff --git a/db/changes/10470-family/01-tableConfig.sql b/db/changes/10470-family/01-tableConfig.sql deleted file mode 100644 index 685981d90..000000000 --- a/db/changes/10470-family/01-tableConfig.sql +++ /dev/null @@ -1 +0,0 @@ -RENAME TABLE `edi`.`fileConfig` to `edi`.`tableConfig`; \ No newline at end of file diff --git a/db/changes/10470-family/02-fileConfig.sql b/db/changes/10470-family/02-fileConfig.sql deleted file mode 100644 index 3109a4616..000000000 --- a/db/changes/10470-family/02-fileConfig.sql +++ /dev/null @@ -1,22 +0,0 @@ -CREATE TABLE `edi`.`fileConfig` -( - name varchar(25) NOT NULL, - checksum text NULL, - keyValue tinyint(1) default true NOT NULL, - constraint fileConfig_pk - primary key (name) -); - -create unique index fileConfig_name_uindex - on `edi`.`fileConfig` (name); - - -INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue) -VALUES ('FEC010104', null, 0); - -INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue) -VALUES ('VBN020101', null, 1); - -INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue) -VALUES ('florecompc2', null, 1); - diff --git a/db/changes/10471-family/00-chat.sql b/db/changes/10471-family/00-chat.sql deleted file mode 100644 index 6f3c9d437..000000000 --- a/db/changes/10471-family/00-chat.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE `vn`.`chat` MODIFY COLUMN message TEXT CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL; -ALTER TABLE `vn`.`chat` MODIFY COLUMN dated DATETIME DEFAULT NULL NULL; -ALTER TABLE `vn`.`chat` ADD error TEXT NULL; diff --git a/db/changes/10472-family/00-creditInsurance.sql b/db/changes/10472-family/00-creditInsurance.sql deleted file mode 100644 index 4731cfb2a..000000000 --- a/db/changes/10472-family/00-creditInsurance.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE `vn`.`creditInsurance` - ADD CONSTRAINT `creditInsurance_creditClassificationFk` FOREIGN KEY (`creditClassificationFk`) - REFERENCES `vn`.`creditClassification` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/db/changes/10480-june/00-ACL.sql b/db/changes/10480-june/00-ACL.sql deleted file mode 100644 index b13e56e21..000000000 --- a/db/changes/10480-june/00-ACL.sql +++ /dev/null @@ -1,21 +0,0 @@ -INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId) - VALUES - ('InvoiceOut','refund','WRITE','ALLOW','ROLE','invoicing'), - ('InvoiceOut','refund','WRITE','ALLOW','ROLE','salesAssistant'), - ('InvoiceOut','refund','WRITE','ALLOW','ROLE','claimManager'), - ('Ticket','refund','WRITE','ALLOW','ROLE','invoicing'), - ('Ticket','refund','WRITE','ALLOW','ROLE','salesAssistant'), - ('Ticket','refund','WRITE','ALLOW','ROLE','claimManager'), - ('Sale','refund','WRITE','ALLOW','ROLE','salesAssistant'), - ('Sale','refund','WRITE','ALLOW','ROLE','claimManager'), - ('TicketRefund','*','WRITE','ALLOW','ROLE','invoicing'), - ('ClaimObservation','*','WRITE','ALLOW','ROLE','salesPerson'), - ('ClaimObservation','*','READ','ALLOW','ROLE','salesPerson'), - ('Client','setPassword','WRITE','ALLOW','ROLE','salesPerson'), - ('Client','updateUser','WRITE','ALLOW','ROLE','salesPerson'); - -DELETE FROM `salix`.`ACL` WHERE id=313; - -UPDATE `salix`.`ACL` - SET principalId='invoicing' - WHERE id=297; \ No newline at end of file diff --git a/db/changes/10480-june/00-aclShelvingLog.sql b/db/changes/10480-june/00-aclShelvingLog.sql deleted file mode 100644 index dc75142d1..000000000 --- a/db/changes/10480-june/00-aclShelvingLog.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId) - VALUES - ('ShelvingLog','*','READ','ALLOW','ROLE','employee'); \ No newline at end of file diff --git a/db/changes/10480-june/00-aclZoneExclusionGeos.sql b/db/changes/10480-june/00-aclZoneExclusionGeos.sql deleted file mode 100644 index 4c0f6c991..000000000 --- a/db/changes/10480-june/00-aclZoneExclusionGeos.sql +++ /dev/null @@ -1,4 +0,0 @@ -INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) - VALUES - ('ZoneExclusionGeo', '*', 'READ', 'ALLOW', 'ROLE', 'employee'), - ('ZoneExclusionGeo', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss'); \ No newline at end of file diff --git a/db/changes/10480-june/00-albaran_gestdoc.sql b/db/changes/10480-june/00-albaran_gestdoc.sql deleted file mode 100644 index a0ba93bd3..000000000 --- a/db/changes/10480-june/00-albaran_gestdoc.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `vn2008`.`albaran_gestdoc` DROP FOREIGN KEY fk_albaran_gestdoc_gestdoc1; -ALTER TABLE `vn2008`.`albaran_gestdoc` ADD CONSTRAINT albaran_gestdoc_FK FOREIGN KEY (gestdoc_id) REFERENCES `vn`.`dms`(id) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/db/changes/10480-june/00-client.sql b/db/changes/10480-june/00-client.sql deleted file mode 100644 index 4a39bbdf9..000000000 --- a/db/changes/10480-june/00-client.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter table `vn`.`client` - add hasIncoterms tinyint(1) default 0 not null comment 'Received incoterms authorization from client'; - diff --git a/db/changes/10480-june/00-deprecations.sql b/db/changes/10480-june/00-deprecations.sql deleted file mode 100644 index 68becd13e..000000000 --- a/db/changes/10480-june/00-deprecations.sql +++ /dev/null @@ -1,13 +0,0 @@ -DROP FUNCTION `account`.`userGetId`; -DROP FUNCTION `account`.`myUserGetName`; -DROP FUNCTION `account`.`myUserGetId`; -DROP FUNCTION `account`.`myUserHasRole`; -DROP FUNCTION `account`.`myUserHasRoleId`; -DROP FUNCTION `account`.`userGetName`; -DROP FUNCTION `account`.`userHasRole`; -DROP FUNCTION `account`.`userHasRoleId`; -DROP PROCEDURE `account`.`myUserLogout`; -DROP PROCEDURE `account`.`userLogin`; -DROP PROCEDURE `account`.`userLoginWithKey`; -DROP PROCEDURE `account`.`userLoginWithName`; -DROP PROCEDURE `account`.`userSetPassword`; \ No newline at end of file diff --git a/db/changes/10480-june/00-item.sql b/db/changes/10480-june/00-item.sql deleted file mode 100644 index a08d3f4c1..000000000 --- a/db/changes/10480-june/00-item.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE `vn`.`item` MODIFY COLUMN description TEXT CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL; diff --git a/db/changes/10480-june/00-route.sql b/db/changes/10480-june/00-route.sql deleted file mode 100644 index beb7d5e41..000000000 --- a/db/changes/10480-june/00-route.sql +++ /dev/null @@ -1,10 +0,0 @@ -UPDATE `vn`.`route` r - JOIN(SELECT r.id, wl.workcenterFk - FROM `vn`.`route` r - JOIN `vn`.`routeLog` rl ON rl.originFk = r.id - JOIN `vn`.`workerLabour` wl ON wl.workerFk = rl.userFk - AND r.created BETWEEN wl.started AND IFNULL(wl.ended, r.created) - WHERE r.created BETWEEN '2021-12-01' AND CURDATE() - AND rl.action = 'insert' - )sub ON sub.id = r.id - SET r.commissionWorkCenterFk = sub.workcenterFk; \ No newline at end of file diff --git a/db/changes/10480-june/00-sample.sql b/db/changes/10480-june/00-sample.sql deleted file mode 100644 index 18beb736d..000000000 --- a/db/changes/10480-june/00-sample.sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO `vn`.`sample` (code, description, isVisible, hasCompany, hasPreview, datepickerEnabled) -VALUES ('incoterms-authorization', 'Autorización de incoterms', 1, 1, 1, 0); \ No newline at end of file diff --git a/db/changes/10480-june/00-shelving.sql b/db/changes/10480-june/00-shelving.sql deleted file mode 100644 index c66d164c4..000000000 --- a/db/changes/10480-june/00-shelving.sql +++ /dev/null @@ -1,18 +0,0 @@ -ALTER TABLE `vn`.`itemShelving` DROP FOREIGN KEY itemShelving_fk2; -ALTER TABLE `vn`.`shelvingLog` DROP FOREIGN KEY shelvingLog_FK_ibfk_1; -ALTER TABLE `vn`.`smartTag` DROP FOREIGN KEY smartTag_shelving_fk; -ALTER TABLE `vn`.`workerShelving` DROP FOREIGN KEY workerShelving_shelving_fk; - -ALTER TABLE `vn`.`shelving` DROP PRIMARY KEY; -ALTER TABLE `vn`.`shelving` ADD id INT auto_increment PRIMARY KEY NULL; -ALTER TABLE `vn`.`shelving` CHANGE id id int(11) auto_increment NOT NULL FIRST; -ALTER TABLE `vn`.`shelving` ADD CONSTRAINT shelving_UN UNIQUE KEY (code); - -ALTER TABLE `vn`.`itemShelving` ADD CONSTRAINT itemShelving_fk2 FOREIGN KEY (shelvingFk) REFERENCES `vn`.`shelving`(code) ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE `vn`.`shelvingLog` ADD CONSTRAINT shelvingLog_FK_ibfk_1 FOREIGN KEY (originFk) REFERENCES `vn`.`shelving`(code) ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE `vn`.`smartTag` ADD CONSTRAINT smartTag_FK FOREIGN KEY (shelvingFk) REFERENCES `vn`.`shelving`(code) ON DELETE RESTRICT ON UPDATE CASCADE; -ALTER TABLE `vn`.`workerShelving` ADD CONSTRAINT workerShelving_FK_1 FOREIGN KEY (shelvingFk) REFERENCES `vn`.`shelving`(code) ON DELETE RESTRICT ON UPDATE CASCADE; - -ALTER TABLE vn.shelvingLog DROP FOREIGN KEY shelvingLog_FK_ibfk_1; -ALTER TABLE vn.shelvingLog MODIFY COLUMN originFk INT NOT NULL; -ALTER TABLE vn.shelvingLog ADD CONSTRAINT shelvingLog_FK FOREIGN KEY (originFk) REFERENCES vn.shelving(id) ON DELETE CASCADE ON UPDATE CASCADE; \ No newline at end of file diff --git a/db/changes/10480-june/00-ticketRefund_beforeUpsert.sql b/db/changes/10480-june/00-ticketRefund_beforeUpsert.sql deleted file mode 100644 index e6506c5d7..000000000 --- a/db/changes/10480-june/00-ticketRefund_beforeUpsert.sql +++ /dev/null @@ -1,21 +0,0 @@ -DROP PROCEDURE IF EXISTS `vn`.`ticketRefund_beforeUpsert`; - -DELIMITER $$ -$$ -CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketRefund_beforeUpsert`(vRefundTicketFk INT, vOriginalTicketFk INT) -BEGIN - DECLARE vAlreadyExists BOOLEAN DEFAULT FALSE; - - IF vRefundTicketFk = vOriginalTicketFk THEN - CALL util.throw('Original ticket and refund ticket has same id'); - END IF; - - SELECT COUNT(*) INTO vAlreadyExists - FROM ticketRefund - WHERE originalTicketFk = vOriginalTicketFk; - - IF vAlreadyExists > 0 THEN - CALL util.throw('This ticket is already a refund'); - END IF; -END$$ -DELIMITER ; diff --git a/db/changes/10480-june/01-claimObservation.sql b/db/changes/10480-june/01-claimObservation.sql deleted file mode 100644 index 8dc126a9e..000000000 --- a/db/changes/10480-june/01-claimObservation.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE `vn`.`claimObservation` ( - `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `claimFk` int(10) unsigned NOT NULL, - `workerFk` int(10) unsigned DEFAULT NULL, - `text` text COLLATE utf8_unicode_ci NOT NULL, - `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - KEY `worker_key` (`workerFk`), - KEY `claim_key` (`claimFk`), - KEY `claimObservation_created_IDX` (`created`) USING BTREE, - CONSTRAINT `claimObservation_ibfk_1` FOREIGN KEY (`claimFk`) REFERENCES `vn`.`claim` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `claimObservation_ibfk_2` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE -) COMMENT='Todas las observaciones referentes a una reclamación' \ No newline at end of file diff --git a/db/changes/10480-june/02-claimTextMigration.sql b/db/changes/10480-june/02-claimTextMigration.sql deleted file mode 100644 index fa5f6fe83..000000000 --- a/db/changes/10480-june/02-claimTextMigration.sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO `vn`.`claimObservation` (`claimFk`, `text`, `created`) -SELECT `id`, `observation`, `created` FROM `vn`.`claim` \ No newline at end of file diff --git a/db/changes/10480-june/04-aclParking.sql b/db/changes/10480-june/04-aclParking.sql deleted file mode 100644 index 05acd68b1..000000000 --- a/db/changes/10480-june/04-aclParking.sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId) - VALUES ('Parking','*','*','ALLOW','ROLE','employee') \ No newline at end of file diff --git a/db/changes/10480-june/04-aclShelving.sql b/db/changes/10480-june/04-aclShelving.sql deleted file mode 100644 index b237dfe0d..000000000 --- a/db/changes/10480-june/04-aclShelving.sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId) - VALUES ('Shelving','*','*','ALLOW','ROLE','employee') \ No newline at end of file diff --git a/db/changes/10481-june/00-aclOsTicket.sql b/db/changes/10481-june/00-aclOsTicket.sql deleted file mode 100644 index ae2a121f5..000000000 --- a/db/changes/10481-june/00-aclOsTicket.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) - VALUES - ('OsTicket', '*', '*', 'ALLOW', 'ROLE', 'employee'); \ No newline at end of file diff --git a/db/changes/10481-june/00-aclOsTicketConfig.sql b/db/changes/10481-june/00-aclOsTicketConfig.sql deleted file mode 100644 index ad53ea6ae..000000000 --- a/db/changes/10481-june/00-aclOsTicketConfig.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) - VALUES - ('OsTicketConfig', '*', '*', 'ALLOW', 'ROLE', 'it'); \ No newline at end of file diff --git a/db/changes/10481-june/00-osTicketConfig.sql b/db/changes/10481-june/00-osTicketConfig.sql deleted file mode 100644 index 8727c816d..000000000 --- a/db/changes/10481-june/00-osTicketConfig.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE TABLE `vn`.`osTicketConfig` ( - `id` int(11) NOT NULL, - `host` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `user` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `password` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `oldStatus` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `newStatusId` int(11) DEFAULT NULL, - `action` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `day` int(11) DEFAULT NULL, - `comment` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `hostDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `userDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `passwordDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL, - `portDb` int(11) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; \ No newline at end of file diff --git a/db/changes/10490-august/00-ACL.sql b/db/changes/224701/00-ACL.sql similarity index 100% rename from db/changes/10490-august/00-ACL.sql rename to db/changes/224701/00-ACL.sql diff --git a/db/changes/10490-august/00-acl_receiptPdf.sql b/db/changes/224701/00-acl_receiptPdf.sql similarity index 100% rename from db/changes/10490-august/00-acl_receiptPdf.sql rename to db/changes/224701/00-acl_receiptPdf.sql diff --git a/db/changes/10490-august/00-clientConsumptionQueue.sql b/db/changes/224701/00-clientConsumptionQueue.sql similarity index 100% rename from db/changes/10490-august/00-clientConsumptionQueue.sql rename to db/changes/224701/00-clientConsumptionQueue.sql diff --git a/db/changes/10490-august/00-invoiceOutQueue.sql b/db/changes/224701/00-invoiceOutQueue.sql similarity index 100% rename from db/changes/10490-august/00-invoiceOutQueue.sql rename to db/changes/224701/00-invoiceOutQueue.sql diff --git a/db/changes/10490-august/00-itemConfig.sql b/db/changes/224701/00-itemConfig.sql similarity index 100% rename from db/changes/10490-august/00-itemConfig.sql rename to db/changes/224701/00-itemConfig.sql diff --git a/db/changes/10490-august/00-printConfig.sql b/db/changes/224701/00-printConfig.sql similarity index 100% rename from db/changes/10490-august/00-printConfig.sql rename to db/changes/224701/00-printConfig.sql diff --git a/db/changes/10490-august/00-sale_afterUpdate.sql b/db/changes/224701/00-sale_afterUpdate.sql similarity index 100% rename from db/changes/10490-august/00-sale_afterUpdate.sql rename to db/changes/224701/00-sale_afterUpdate.sql diff --git a/db/changes/10490-august/00-sample.sql b/db/changes/224701/00-sample.sql similarity index 100% rename from db/changes/10490-august/00-sample.sql rename to db/changes/224701/00-sample.sql diff --git a/db/changes/10490-august/00-user_hasGrant.sql b/db/changes/224701/00-user_hasGrant.sql similarity index 100% rename from db/changes/10490-august/00-user_hasGrant.sql rename to db/changes/224701/00-user_hasGrant.sql diff --git a/db/changes/10491-august/00-ACL_workerDisableExcluded.sql b/db/changes/224702/00-ACL_workerDisableExcluded.sql similarity index 100% rename from db/changes/10491-august/00-ACL_workerDisableExcluded.sql rename to db/changes/224702/00-ACL_workerDisableExcluded.sql diff --git a/db/changes/10491-august/00-aclBusiness.sql b/db/changes/224702/00-aclBusiness.sql similarity index 100% rename from db/changes/10491-august/00-aclBusiness.sql rename to db/changes/224702/00-aclBusiness.sql diff --git a/db/changes/10491-august/00-aclUsesMana.sql b/db/changes/224702/00-aclUsesMana.sql similarity index 100% rename from db/changes/10491-august/00-aclUsesMana.sql rename to db/changes/224702/00-aclUsesMana.sql diff --git a/db/changes/10491-august/00-defaultPayDem_sameAs_production.sql b/db/changes/224702/00-defaultPayDem_sameAs_production.sql similarity index 100% rename from db/changes/10491-august/00-defaultPayDem_sameAs_production.sql rename to db/changes/224702/00-defaultPayDem_sameAs_production.sql diff --git a/db/changes/10491-august/00-invoiceInPdf.sql b/db/changes/224702/00-invoiceInPdf.sql similarity index 100% rename from db/changes/10491-august/00-invoiceInPdf.sql rename to db/changes/224702/00-invoiceInPdf.sql diff --git a/db/changes/10491-august/00-newSupplier_ACL.sql b/db/changes/224702/00-newSupplier_ACL.sql similarity index 100% rename from db/changes/10491-august/00-newSupplier_ACL.sql rename to db/changes/224702/00-newSupplier_ACL.sql diff --git a/db/changes/10491-august/00-notificationProc.sql b/db/changes/224702/00-notificationProc.sql similarity index 100% rename from db/changes/10491-august/00-notificationProc.sql rename to db/changes/224702/00-notificationProc.sql diff --git a/db/changes/10491-august/00-notificationTables.sql b/db/changes/224702/00-notificationTables.sql similarity index 100% rename from db/changes/10491-august/00-notificationTables.sql rename to db/changes/224702/00-notificationTables.sql diff --git a/db/changes/10491-august/00-payMethodFk_Allow_Null.sql b/db/changes/224702/00-payMethodFk_Allow_Null.sql similarity index 100% rename from db/changes/10491-august/00-payMethodFk_Allow_Null.sql rename to db/changes/224702/00-payMethodFk_Allow_Null.sql diff --git a/db/changes/10491-august/00-supplierActivityFk_Allow_Null.sql b/db/changes/224702/00-supplierActivityFk_Allow_Null.sql similarity index 100% rename from db/changes/10491-august/00-supplierActivityFk_Allow_Null.sql rename to db/changes/224702/00-supplierActivityFk_Allow_Null.sql diff --git a/db/changes/10491-august/00-ticket_closeByTicket.sql b/db/changes/224702/00-ticket_closeByTicket.sql similarity index 100% rename from db/changes/10491-august/00-ticket_closeByTicket.sql rename to db/changes/224702/00-ticket_closeByTicket.sql diff --git a/db/changes/10491-august/00-zipConfig.sql b/db/changes/224702/00-zipConfig.sql similarity index 100% rename from db/changes/10491-august/00-zipConfig.sql rename to db/changes/224702/00-zipConfig.sql diff --git a/db/changes/10500-november/00-ACL.sql b/db/changes/224801/00-ACL.sql similarity index 100% rename from db/changes/10500-november/00-ACL.sql rename to db/changes/224801/00-ACL.sql diff --git a/db/changes/10500-november/00-claim.sql b/db/changes/224801/00-claim.sql similarity index 100% rename from db/changes/10500-november/00-claim.sql rename to db/changes/224801/00-claim.sql diff --git a/db/changes/10500-november/00-claimRma.sql b/db/changes/224801/00-claimRma.sql similarity index 100% rename from db/changes/10500-november/00-claimRma.sql rename to db/changes/224801/00-claimRma.sql diff --git a/db/changes/10501-november/00-aclNotification.sql b/db/changes/224901/00-aclNotification.sql similarity index 100% rename from db/changes/10501-november/00-aclNotification.sql rename to db/changes/224901/00-aclNotification.sql diff --git a/db/changes/10501-november/00-packingSiteConfig.sql b/db/changes/224901/00-packingSiteConfig.sql similarity index 100% rename from db/changes/10501-november/00-packingSiteConfig.sql rename to db/changes/224901/00-packingSiteConfig.sql diff --git a/db/changes/10501-november/00-packingSiteUpdate.sql b/db/changes/224901/00-packingSiteUpdate.sql similarity index 100% rename from db/changes/10501-november/00-packingSiteUpdate.sql rename to db/changes/224901/00-packingSiteUpdate.sql diff --git a/db/changes/10501-november/00-salix_url.sql b/db/changes/224901/00-salix_url.sql similarity index 100% rename from db/changes/10501-november/00-salix_url.sql rename to db/changes/224901/00-salix_url.sql diff --git a/db/changes/224902/00-aclUserPassword.sql b/db/changes/224902/00-aclUserPassword.sql new file mode 100644 index 000000000..b92b54c28 --- /dev/null +++ b/db/changes/224902/00-aclUserPassword.sql @@ -0,0 +1,2 @@ +DELETE FROM `salix`.`ACL` + WHERE model = 'UserPassword'; diff --git a/db/changes/10502-november/00-deletePickupContact.sql b/db/changes/224902/00-deletePickupContact.sql similarity index 100% rename from db/changes/10502-november/00-deletePickupContact.sql rename to db/changes/224902/00-deletePickupContact.sql diff --git a/db/changes/10502-november/00-itemShelvingACL.sql b/db/changes/224902/00-itemShelvingACL.sql similarity index 100% rename from db/changes/10502-november/00-itemShelvingACL.sql rename to db/changes/224902/00-itemShelvingACL.sql diff --git a/db/changes/10502-november/00-itemShelvingPlacementSupplyStockACL.sql b/db/changes/224902/00-itemShelvingPlacementSupplyStockACL.sql similarity index 100% rename from db/changes/10502-november/00-itemShelvingPlacementSupplyStockACL.sql rename to db/changes/224902/00-itemShelvingPlacementSupplyStockACL.sql diff --git a/db/changes/10502-november/00-workerTimeControlMail.sql b/db/changes/224902/00-workerTimeControlMail.sql similarity index 100% rename from db/changes/10502-november/00-workerTimeControlMail.sql rename to db/changes/224902/00-workerTimeControlMail.sql diff --git a/db/changes/10502-november/00-zone_getPostalCode.sql b/db/changes/224902/00-zone_getPostalCode.sql similarity index 100% rename from db/changes/10502-november/00-zone_getPostalCode.sql rename to db/changes/224902/00-zone_getPostalCode.sql diff --git a/db/changes/10503-november/00-ACL_notification_InvoiceE.sql b/db/changes/224903/00-ACL_notification_InvoiceE.sql similarity index 100% rename from db/changes/10503-november/00-ACL_notification_InvoiceE.sql rename to db/changes/224903/00-ACL_notification_InvoiceE.sql diff --git a/db/changes/10503-november/00-aclInvoiceOut.sql b/db/changes/224903/00-aclInvoiceOut.sql similarity index 100% rename from db/changes/10503-november/00-aclInvoiceOut.sql rename to db/changes/224903/00-aclInvoiceOut.sql diff --git a/db/changes/10503-november/00-alter_expedition_itemFk.sql b/db/changes/224903/00-alter_expedition_itemFk.sql similarity index 100% rename from db/changes/10503-november/00-alter_expedition_itemFk.sql rename to db/changes/224903/00-alter_expedition_itemFk.sql diff --git a/db/changes/10503-november/00-clientHasInvoiceElectronic.sql b/db/changes/224903/00-clientHasInvoiceElectronic.sql similarity index 100% rename from db/changes/10503-november/00-clientHasInvoiceElectronic.sql rename to db/changes/224903/00-clientHasInvoiceElectronic.sql diff --git a/db/changes/10503-november/00-collection_missingTrash.sql b/db/changes/224903/00-collection_missingTrash.sql similarity index 100% rename from db/changes/10503-november/00-collection_missingTrash.sql rename to db/changes/224903/00-collection_missingTrash.sql diff --git a/db/changes/10503-november/00-deleteInvoiceOutQueue.sql b/db/changes/224903/00-deleteInvoiceOutQueue.sql similarity index 100% rename from db/changes/10503-november/00-deleteInvoiceOutQueue.sql rename to db/changes/224903/00-deleteInvoiceOutQueue.sql diff --git a/db/changes/10503-november/00-editTrackedACL.sql b/db/changes/224903/00-editTrackedACL.sql similarity index 100% rename from db/changes/10503-november/00-editTrackedACL.sql rename to db/changes/224903/00-editTrackedACL.sql diff --git a/db/changes/10503-november/00-greuge.sql b/db/changes/224903/00-greuge.sql similarity index 100% rename from db/changes/10503-november/00-greuge.sql rename to db/changes/224903/00-greuge.sql diff --git a/db/changes/10503-november/00-insert_notification_invoiceE.sql b/db/changes/224903/00-insert_notification_invoiceE.sql similarity index 100% rename from db/changes/10503-november/00-insert_notification_invoiceE.sql rename to db/changes/224903/00-insert_notification_invoiceE.sql diff --git a/db/changes/10503-november/00-isCompensationACL.sql b/db/changes/224903/00-isCompensationACL.sql similarity index 100% rename from db/changes/10503-november/00-isCompensationACL.sql rename to db/changes/224903/00-isCompensationACL.sql diff --git a/db/changes/10503-november/00-osTicketConfig.sql b/db/changes/224903/00-osTicketConfig.sql similarity index 100% rename from db/changes/10503-november/00-osTicketConfig.sql rename to db/changes/224903/00-osTicketConfig.sql diff --git a/db/changes/10503-november/00-ticket_canMerge.sql b/db/changes/224903/00-ticket_canMerge.sql similarity index 100% rename from db/changes/10503-november/00-ticket_canMerge.sql rename to db/changes/224903/00-ticket_canMerge.sql diff --git a/db/changes/10503-november/00-ticket_canbePostponed.sql b/db/changes/224903/00-ticket_canbePostponed.sql similarity index 100% rename from db/changes/10503-november/00-ticket_canbePostponed.sql rename to db/changes/224903/00-ticket_canbePostponed.sql diff --git a/db/changes/10503-november/00-timeBusiness_calculate.sql b/db/changes/224903/00-timeBusiness_calculate.sql similarity index 100% rename from db/changes/10503-november/00-timeBusiness_calculate.sql rename to db/changes/224903/00-timeBusiness_calculate.sql diff --git a/db/changes/10503-november/01-updateClientHasInvoiceElectronic.sql b/db/changes/224903/01-updateClientHasInvoiceElectronic.sql similarity index 100% rename from db/changes/10503-november/01-updateClientHasInvoiceElectronic.sql rename to db/changes/224903/01-updateClientHasInvoiceElectronic.sql diff --git a/db/changes/225001/.gitkeep b/db/changes/225001/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/db/dump/dumpedFixtures.sql b/db/dump/dumpedFixtures.sql index c9c265ae7..a4870e2df 100644 --- a/db/dump/dumpedFixtures.sql +++ b/db/dump/dumpedFixtures.sql @@ -22,7 +22,7 @@ USE `util`; LOCK TABLES `config` WRITE; /*!40000 ALTER TABLE `config` DISABLE KEYS */; -INSERT INTO `config` VALUES (1,'10480',0,'production',NULL); +INSERT INTO `config` VALUES (1,'224602',0,'production',NULL); /*!40000 ALTER TABLE `config` ENABLE KEYS */; UNLOCK TABLES; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 482218ed2..92b84be43 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2564,10 +2564,6 @@ UPDATE `vn`.`route` UPDATE `vn`.`route` SET `invoiceInFk`=2 WHERE `id`=2; -INSERT INTO `bs`.`salesPerson` (`workerFk`, `year`, `month`, `portfolioWeight`) - VALUES - (18, YEAR(util.VN_CURDATE()), MONTH(util.VN_CURDATE()), 807.23), - (19, YEAR(util.VN_CURDATE()), MONTH(util.VN_CURDATE()), 34.40); INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`) VALUES diff --git a/db/dump/structure.sql b/db/dump/structure.sql index 6d85d0511..403534787 100644 --- a/db/dump/structure.sql +++ b/db/dump/structure.sql @@ -81044,4 +81044,3 @@ USE `vncontrol`; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2022-09-16 10:44:31 - diff --git a/db/import-changes.sh b/db/import-changes.sh index 2b80654d3..5461f003b 100755 --- a/db/import-changes.sh +++ b/db/import-changes.sh @@ -81,9 +81,9 @@ N_CHANGES=0 for DIR_PATH in "$DIR/changes/"*; do DIR_NAME=$(basename $DIR_PATH) - DIR_VERSION=${DIR_NAME:0:5} + DIR_VERSION=${DIR_NAME:0:6} - if [[ ! "$DIR_NAME" =~ ^[0-9]{5}(-[a-zA-Z0-9]+)?$ ]]; then + if [[ ! "$DIR_NAME" =~ ^[0-9]{6}$ ]]; then echo "[WARN] Ignoring wrong directory name: $DIR_NAME" continue fi diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 06fabe3e6..f550e3a9d 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -29,6 +29,11 @@ export default { firstModulePinIcon: 'vn-home a:nth-child(1) vn-icon[icon="push_pin"]', firstModuleRemovePinIcon: 'vn-home a:nth-child(1) vn-icon[icon="remove_circle"]' }, + recoverPassword: { + recoverPasswordButton: 'vn-login a[ui-sref="recoverPassword"]', + email: 'vn-recover-password vn-textfield[ng-model="$ctrl.email"]', + sendEmailButton: 'vn-recover-password vn-submit', + }, accountIndex: { addAccount: 'vn-user-index button vn-icon[icon="add"]', newName: 'vn-user-create vn-textfield[ng-model="$ctrl.user.name"]', diff --git a/e2e/paths/01-salix/04_recoverPassword.spec.js b/e2e/paths/01-salix/04_recoverPassword.spec.js new file mode 100644 index 000000000..67e6362d3 --- /dev/null +++ b/e2e/paths/01-salix/04_recoverPassword.spec.js @@ -0,0 +1,41 @@ +import selectors from '../../helpers/selectors'; +import getBrowser from '../../helpers/puppeteer'; + +// https://redmine.verdnatura.es/issues/4995 fix login +xdescribe('RecoverPassword path', async() => { + let browser; + let page; + + beforeAll(async() => { + browser = await getBrowser(); + page = browser.page; + + await page.waitToClick(selectors.recoverPassword.recoverPasswordButton); + await page.waitForState('recoverPassword'); + }); + + afterAll(async() => { + await browser.close(); + }); + + it('should not throw error if not exist user', async() => { + await page.write(selectors.recoverPassword.email, 'fakeEmail@mydomain.com'); + await page.waitToClick(selectors.recoverPassword.sendEmailButton); + + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Notification sent!'); + }); + + it('should send email', async() => { + await page.waitForState('login'); + await page.waitToClick(selectors.recoverPassword.recoverPasswordButton); + + await page.write(selectors.recoverPassword.email, 'BruceWayne@mydomain.com'); + await page.waitToClick(selectors.recoverPassword.sendEmailButton); + const message = await page.waitForSnackbar(); + await page.waitForState('login'); + + expect(message.text).toContain('Notification sent!'); + }); +}); diff --git a/e2e/paths/05-ticket/20_future.spec.js b/e2e/paths/05-ticket/20_future.spec.js index 4fee9523b..6db2bf4f0 100644 --- a/e2e/paths/05-ticket/20_future.spec.js +++ b/e2e/paths/05-ticket/20_future.spec.js @@ -5,80 +5,78 @@ describe('Ticket Future path', () => { let browser; let page; - beforeAll(async () => { + beforeAll(async() => { browser = await getBrowser(); page = browser.page; await page.loginAndModule('employee', 'ticket'); await page.accessToSection('ticket.future'); }); - afterAll(async () => { + afterAll(async() => { await browser.close(); }); const now = new Date(); const tomorrow = new Date(now.getDate() + 1); - const ticket = { - originDated: now, - futureDated: now, - linesMax: '9999', - litersMax: '9999', - warehouseFk: 'Warehouse One' - }; - it('should show errors snackbar because of the required data', async () => { + it('should show errors snackbar because of the required data', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.warehouseFk); await page.waitToClick(selectors.ticketFuture.submit); let message = await page.waitForSnackbar(); + expect(message.text).toContain('warehouseFk is a required argument'); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.litersMax); await page.waitToClick(selectors.ticketFuture.submit); message = await page.waitForSnackbar(); + expect(message.text).toContain('litersMax is a required argument'); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.linesMax); await page.waitToClick(selectors.ticketFuture.submit); message = await page.waitForSnackbar(); + expect(message.text).toContain('linesMax is a required argument'); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.futureDated); await page.waitToClick(selectors.ticketFuture.submit); message = await page.waitForSnackbar(); + expect(message.text).toContain('futureDated is a required argument'); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.originDated); await page.waitToClick(selectors.ticketFuture.submit); message = await page.waitForSnackbar(); + expect(message.text).toContain('originDated is a required argument'); }); - it('should search with the required data', async () => { + it('should search with the required data', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search with the origin shipped today', async () => { + it('should search with the origin shipped today', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.pickDate(selectors.ticketFuture.shipped, now); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search with the origin shipped tomorrow', async () => { + it('should search with the origin shipped tomorrow', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.pickDate(selectors.ticketFuture.shipped, tomorrow); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); }); - it('should search with the destination shipped today', async () => { + it('should search with the destination shipped today', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.shipped); await page.pickDate(selectors.ticketFuture.tfShipped, now); @@ -86,14 +84,14 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search with the destination shipped tomorrow', async () => { + it('should search with the destination shipped tomorrow', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.pickDate(selectors.ticketFuture.tfShipped, tomorrow); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); }); - it('should search with the origin IPT', async () => { + it('should search with the origin IPT', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.shipped); @@ -108,7 +106,7 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); }); - it('should search with the destination IPT', async () => { + it('should search with the destination IPT', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.shipped); @@ -123,7 +121,7 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); }); - it('should search with the origin grouped state', async () => { + it('should search with the origin grouped state', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.shipped); @@ -138,7 +136,7 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 3); }); - it('should search with the destination grouped state', async () => { + it('should search with the destination grouped state', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.shipped); @@ -164,10 +162,10 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search in smart-table with an ID Origin', async () => { + it('should search in smart-table with an ID Origin', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableId, "13"); - await page.keyboard.press("Enter"); + await page.write(selectors.ticketFuture.tableId, '13'); + await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 2); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); @@ -176,10 +174,10 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search in smart-table with an ID Destination', async () => { + it('should search in smart-table with an ID Destination', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableTfId, "12"); - await page.keyboard.press("Enter"); + await page.write(selectors.ticketFuture.tableTfId, '12'); + await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 5); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); @@ -188,7 +186,7 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search in smart-table with an IPT Origin', async () => { + it('should search in smart-table with an IPT Origin', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); await page.autocompleteSearch(selectors.ticketFuture.tableIpt, 'Vertical'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 1); @@ -199,7 +197,7 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search in smart-table with an IPT Destination', async () => { + it('should search in smart-table with an IPT Destination', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); await page.autocompleteSearch(selectors.ticketFuture.tableTfIpt, 'Vertical'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 1); @@ -210,10 +208,10 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search in smart-table with especified Lines', async () => { + it('should search in smart-table with especified Lines', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableLines, "0"); - await page.keyboard.press("Enter"); + await page.write(selectors.ticketFuture.tableLines, '0'); + await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 1); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); @@ -222,8 +220,8 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableLines, "1"); - await page.keyboard.press("Enter"); + await page.write(selectors.ticketFuture.tableLines, '1'); + await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 5); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); @@ -232,10 +230,10 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search in smart-table with especified Liters', async () => { + it('should search in smart-table with especified Liters', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableLiters, "0"); - await page.keyboard.press("Enter"); + await page.write(selectors.ticketFuture.tableLiters, '0'); + await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 1); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); @@ -244,8 +242,8 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableLiters, "28"); - await page.keyboard.press("Enter"); + await page.write(selectors.ticketFuture.tableLiters, '28'); + await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 5); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); @@ -254,13 +252,13 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should check the three last tickets and move to the future', async () => { + it('should check the three last tickets and move to the future', async() => { await page.waitToClick(selectors.ticketFuture.multiCheck); await page.waitToClick(selectors.ticketFuture.firstCheck); await page.waitToClick(selectors.ticketFuture.moveButton); await page.waitToClick(selectors.ticketFuture.acceptButton); const message = await page.waitForSnackbar(); + expect(message.text).toContain('Tickets moved successfully!'); }); - }); diff --git a/front/core/lib/component.js b/front/core/lib/component.js index f17db68a2..5695d9449 100644 --- a/front/core/lib/component.js +++ b/front/core/lib/component.js @@ -12,9 +12,10 @@ export default class Component extends EventEmitter { * @param {HTMLElement} $element The main component element * @param {$rootScope.Scope} $scope The element scope * @param {Function} $transclude The transclusion function + * @param {Function} $location The location function */ - constructor($element, $scope, $transclude) { - super(); + constructor($element, $scope, $transclude, $location) { + super($element, $scope, $transclude, $location); this.$ = $scope; if (!$element) return; @@ -164,7 +165,7 @@ export default class Component extends EventEmitter { $transclude.$$boundTransclude.$$slots[slot]; } } -Component.$inject = ['$element', '$scope']; +Component.$inject = ['$element', '$scope', '$location', '$state']; /* * Automatically adds the most used services to the prototype, so they are diff --git a/front/core/services/auth.js b/front/core/services/auth.js index a1dcfa395..04520cd0b 100644 --- a/front/core/services/auth.js +++ b/front/core/services/auth.js @@ -23,7 +23,10 @@ export default class Auth { initialize() { let criteria = { - to: state => state.name != 'login' + to: state => { + const outLayout = ['login', 'recoverPassword', 'resetPassword']; + return !outLayout.some(ol => ol == state.name); + } }; this.$transitions.onStart(criteria, transition => { if (this.loggedIn) diff --git a/front/salix/components/app/app.js b/front/salix/components/app/app.js index 1f8cdb46e..91a8d2215 100644 --- a/front/salix/components/app/app.js +++ b/front/salix/components/app/app.js @@ -9,13 +9,20 @@ import Component from 'core/lib/component'; * @property {SideMenu} rightMenu The left menu, if it's present */ export default class App extends Component { + constructor($element, $, $location, $state) { + super($element, $, $location, $state); + this.$location = $location; + this.$state = $state; + } + $postLink() { this.vnApp.logger = this; } get showLayout() { - let state = this.$state.current.name; - return state && state != 'login'; + const state = this.$state.current.name || this.$location.$$path.substring(1).replace('/', '.'); + const outLayout = ['login', 'recoverPassword', 'resetPassword', 'reset-password']; + return state && !outLayout.some(ol => ol == state); } $onDestroy() { diff --git a/front/salix/components/index.js b/front/salix/components/index.js index ce4ad585a..dbe9fe81a 100644 --- a/front/salix/components/index.js +++ b/front/salix/components/index.js @@ -5,7 +5,10 @@ import './descriptor-popover'; import './home/home'; import './layout'; import './left-menu/left-menu'; +import './login/index'; import './login/login'; +import './login/recover-password'; +import './login/reset-password'; import './module-card'; import './module-main'; import './side-menu/side-menu'; diff --git a/front/salix/components/login/index.html b/front/salix/components/login/index.html new file mode 100644 index 000000000..186979f8c --- /dev/null +++ b/front/salix/components/login/index.html @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/front/salix/components/login/index.js b/front/salix/components/login/index.js new file mode 100644 index 000000000..f0e21fa29 --- /dev/null +++ b/front/salix/components/login/index.js @@ -0,0 +1,16 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export default class OutLayout extends Component { + constructor($element, $scope) { + super($element, $scope); + } +} + +OutLayout.$inject = ['$element', '$scope']; + +ngModule.vnComponent('vnOutLayout', { + template: require('./index.html'), + controller: OutLayout +}); diff --git a/front/salix/components/login/locale/en.yml b/front/salix/components/login/locale/en.yml index c59a6dd8e..1ddd454b7 100644 --- a/front/salix/components/login/locale/en.yml +++ b/front/salix/components/login/locale/en.yml @@ -1,4 +1,8 @@ User: User Password: Password Do not close session: Do not close session -Enter: Enter \ No newline at end of file +Enter: Enter +Password requirements: > + The password must have at least {{ length }} length characters, + {{nAlpha}} alphabetic characters, {{nUpper}} capital letters, {{nDigits}} + digits and {{nPunct}} symbols (Ex: $%&.) diff --git a/front/salix/components/login/locale/es.yml b/front/salix/components/login/locale/es.yml index 9c9ba5905..e3a5815c1 100644 --- a/front/salix/components/login/locale/es.yml +++ b/front/salix/components/login/locale/es.yml @@ -1,4 +1,16 @@ User: Usuario Password: Contraseña +Email: Correo electrónico Do not close session: No cerrar sesión -Enter: Entrar \ No newline at end of file +Enter: Entrar +I do not remember my password: No recuerdo mi contraseña +Recover password: Recuperar contraseña +We will sent you an email to recover your password: Te enviaremos un correo para restablecer tu contraseña +Notification sent!: ¡Notificación enviada! +Reset password: Restrablecer contraseña +New password: Nueva contraseña +Repeat password: Repetir contraseña +Password requirements: > + La contraseña debe tener al menos {{ length }} caracteres de longitud, + {{nAlpha}} caracteres alfabéticos, {{nUpper}} letras mayúsculas, {{nDigits}} + dígitos y {{nPunct}} símbolos (Ej: $%&.) diff --git a/front/salix/components/login/login.html b/front/salix/components/login/login.html index b15714a23..807e4b284 100644 --- a/front/salix/components/login/login.html +++ b/front/salix/components/login/login.html @@ -1,27 +1,32 @@
- - - - - - - -
+ + + + + + + +
diff --git a/front/salix/components/login/recover-password.html b/front/salix/components/login/recover-password.html new file mode 100644 index 000000000..73f5401d9 --- /dev/null +++ b/front/salix/components/login/recover-password.html @@ -0,0 +1,17 @@ +
Recover password
+ + +
+ We will sent you an email to recover your password +
+ diff --git a/front/salix/components/login/recover-password.js b/front/salix/components/login/recover-password.js new file mode 100644 index 000000000..fa9bfc459 --- /dev/null +++ b/front/salix/components/login/recover-password.js @@ -0,0 +1,37 @@ +import ngModule from '../../module'; +import './style.scss'; + +export default class Controller { + constructor($scope, $element, $http, vnApp, $translate, $state) { + Object.assign(this, { + $scope, + $element, + $http, + vnApp, + $translate, + $state + }); + } + + goToLogin() { + this.vnApp.showSuccess(this.$translate.instant('Notification sent!')); + this.$state.go('login'); + } + + submit() { + const params = { + email: this.email + }; + + this.$http.post('Accounts/recoverPassword', params) + .then(() => { + this.goToLogin(); + }); + } +} +Controller.$inject = ['$scope', '$element', '$http', 'vnApp', '$translate', '$state']; + +ngModule.vnComponent('vnRecoverPassword', { + template: require('./recover-password.html'), + controller: Controller +}); diff --git a/front/salix/components/login/reset-password.html b/front/salix/components/login/reset-password.html new file mode 100644 index 000000000..bdbdc113e --- /dev/null +++ b/front/salix/components/login/reset-password.html @@ -0,0 +1,19 @@ +
Reset password
+ + + + + diff --git a/front/salix/components/login/reset-password.js b/front/salix/components/login/reset-password.js new file mode 100644 index 000000000..9ee1fdb62 --- /dev/null +++ b/front/salix/components/login/reset-password.js @@ -0,0 +1,48 @@ +import ngModule from '../../module'; +import './style.scss'; + +export default class Controller { + constructor($scope, $element, $http, vnApp, $translate, $state, $location) { + Object.assign(this, { + $scope, + $element, + $http, + vnApp, + $translate, + $state, + $location + }); + } + + $onInit() { + this.$http.get('UserPasswords/findOne') + .then(res => { + this.passRequirements = res.data; + }); + } + + submit() { + if (!this.newPassword) + throw new UserError(`You must enter a new password`); + if (this.newPassword != this.repeatPassword) + throw new UserError(`Passwords don't match`); + + const headers = { + Authorization: this.$location.$$search.access_token + }; + + const newPassword = this.newPassword; + + this.$http.post('users/reset-password', {newPassword}, {headers}) + .then(() => { + this.vnApp.showSuccess(this.$translate.instant('Password changed!')); + this.$state.go('login'); + }); + } +} +Controller.$inject = ['$scope', '$element', '$http', 'vnApp', '$translate', '$state', '$location']; + +ngModule.vnComponent('vnResetPassword', { + template: require('./reset-password.html'), + controller: Controller +}); diff --git a/front/salix/components/login/style.scss b/front/salix/components/login/style.scss index 8ebf2a68c..5a7e72245 100644 --- a/front/salix/components/login/style.scss +++ b/front/salix/components/login/style.scss @@ -1,6 +1,31 @@ @import "variables"; -vn-login { +vn-login, +vn-reset-password, +vn-recover-password{ + .footer { + margin-top: 32px; + text-align: center; + position: relative; + & > .vn-submit { + display: block; + + & > input { + display: block; + width: 100%; + } + } + & > .spinner-wrapper { + position: absolute; + width: 0; + top: 3px; + right: -8px; + overflow: visible; + } + } +} + +vn-login{ position: absolute; height: 100%; width: 100%; @@ -39,28 +64,17 @@ vn-login { white-space: inherit; } } - & > .footer { - margin-top: 32px; - text-align: center; - position: relative; - - & > vn-submit { - display: block; - - & > input { - display: block; - width: 100%; - } - } - & > .spinner-wrapper { - position: absolute; - width: 0; - top: 3px; - right: -8px; - overflow: visible; - } - } } + + h5{ + color: $color-primary; + } + + .text-secondary{ + text-align: center; + padding-bottom: 16px; + } + } @media screen and (max-width: 600px) { @@ -71,4 +85,8 @@ vn-login { box-shadow: none; } } + + a{ + color: $color-primary; + } } diff --git a/front/salix/module.js b/front/salix/module.js index a8de61ae0..01df01a67 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -112,7 +112,7 @@ function $exceptionHandler(vnApp, $window, $state, $injector) { switch (exception.status) { case 401: - if ($state.current.name != 'login') { + if (!$state.current.name.includes('login')) { messageT = 'Session has expired'; let params = {continue: $window.location.hash}; $state.go('login', params); diff --git a/front/salix/routes.js b/front/salix/routes.js index 600907ff1..613dc7288 100644 --- a/front/salix/routes.js +++ b/front/salix/routes.js @@ -10,9 +10,19 @@ function config($stateProvider, $urlRouterProvider) { url: '/login?continue', description: 'Login', views: { - login: {template: ''} + 'login': {template: ''}, } }) + .state('recoverPassword', { + url: '/recover-password', + description: 'Recover-password', + template: 'asd' + }) + .state('resetPassword', { + url: '/reset-password', + description: 'Reset-password', + template: '' + }) .state('home', { url: '/', description: 'Home', diff --git a/loopback/locale/en.json b/loopback/locale/en.json index ad110ec98..24a3a40a8 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -131,12 +131,15 @@ "Fichadas impares": "Odd signs", "Descanso diario 9h.": "Daily rest 9h.", "Descanso semanal 36h. / 72h.": "Weekly rest 36h. / 72h.", + "Verify email": "Verify email", + "Click on the following link to verify this email. If you haven't requested this email, just ignore it": "Click on the following link to verify this email. If you haven't requested this email, just ignore it", "Password does not meet requirements": "Password does not meet requirements", "You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies", "Not enough privileges to edit a client": "Not enough privileges to edit a client", "Claim pickup order sent": "Claim pickup order sent [({{claimId}})]({{{claimUrl}}}) to client *{{clientName}}*", "You don't have grant privilege": "You don't have grant privilege", "You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user", + "Email verify": "Email verify", "Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) merged with [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})", "Sale(s) blocked, please contact production": "Sale(s) blocked, please contact production", "Receipt's bank was not found": "Receipt's bank was not found", diff --git a/loopback/locale/es.json b/loopback/locale/es.json index ecaae31b9..932dfe98f 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -245,6 +245,8 @@ "Already has this status": "Ya tiene este estado", "There aren't records for this week": "No existen registros para esta semana", "Empty data source": "Origen de datos vacio", + "Email verify": "Correo de verificación", + "Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment", "Receipt's bank was not found": "No se encontró el banco del recibo", "This receipt was not compensated": "Este recibo no ha sido compensado", "Client's email was not found": "No se encontró el email del cliente" diff --git a/loopback/server/datasources.json b/loopback/server/datasources.json index 4db642058..00f6bf624 100644 --- a/loopback/server/datasources.json +++ b/loopback/server/datasources.json @@ -113,4 +113,4 @@ "application/x-7z-compressed" ] } -} \ No newline at end of file +} diff --git a/modules/account/back/models/user-password.json b/modules/account/back/models/user-password.json index 1b7e49edd..53909ad1f 100644 --- a/modules/account/back/models/user-password.json +++ b/modules/account/back/models/user-password.json @@ -30,5 +30,13 @@ "type": "number", "required": true } - } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] } diff --git a/modules/account/front/basic-data/index.js b/modules/account/front/basic-data/index.js index 342297e45..77d3eab26 100644 --- a/modules/account/front/basic-data/index.js +++ b/modules/account/front/basic-data/index.js @@ -2,6 +2,11 @@ import ngModule from '../module'; import Section from 'salix/components/section'; export default class Controller extends Section { + $onInit() { + if (this.$params.emailConfirmed) + this.vnApp.showSuccess(this.$t('Email verified successfully!')); + } + onSubmit() { this.$.watcher.submit() .then(() => this.card.reload()); diff --git a/modules/account/front/basic-data/locale/es.yml b/modules/account/front/basic-data/locale/es.yml new file mode 100644 index 000000000..2ca7bf698 --- /dev/null +++ b/modules/account/front/basic-data/locale/es.yml @@ -0,0 +1 @@ +Email verified successfully!: Correo verificado correctamente! diff --git a/modules/account/front/routes.json b/modules/account/front/routes.json index b96c931c9..a6f2f5d3f 100644 --- a/modules/account/front/routes.json +++ b/modules/account/front/routes.json @@ -74,7 +74,7 @@ } }, { - "url": "/basic-data", + "url": "/basic-data?emailConfirmed", "state": "account.card.basicData", "component": "vn-user-basic-data", "description": "Basic data", diff --git a/modules/client/back/methods/client/filter.js b/modules/client/back/methods/client/filter.js index 3e1ea43bb..1ae569fd3 100644 --- a/modules/client/back/methods/client/filter.js +++ b/modules/client/back/methods/client/filter.js @@ -91,7 +91,18 @@ module.exports = Self => { case 'search': return /^\d+$/.test(value) ? {'c.id': {inq: value}} - : {'c.name': {like: `%${value}%`}}; + : {or: [ + {'c.name': {like: `%${value}%`}}, + {'c.socialName': {like: `%${value}%`}}, + ]}; + case 'phone': + return {or: [ + {'c.phone': {like: `%${value}%`}}, + {'c.mobile': {like: `%${value}%`}}, + ]}; + case 'zoneFk': + param = 'a.postalCode'; + return {[param]: {inq: postalCode}}; case 'name': case 'salesPersonFk': case 'fi': @@ -100,12 +111,8 @@ module.exports = Self => { case 'postcode': case 'provinceFk': case 'email': - case 'phone': param = `c.${param}`; - return {[param]: value}; - case 'zoneFk': - param = 'a.postalCode'; - return {[param]: {inq: postalCode}}; + return {[param]: {like: `%${value}%`}}; } }); @@ -119,6 +126,7 @@ module.exports = Self => { c.fi, c.socialName, c.phone, + c.mobile, c.city, c.postcode, c.email, @@ -132,7 +140,7 @@ module.exports = Self => { LEFT JOIN account.user u ON u.id = c.salesPersonFk LEFT JOIN province p ON p.id = c.provinceFk JOIN vn.address a ON a.clientFk = c.id - ` + ` ); stmt.merge(conn.makeWhere(filter.where)); diff --git a/modules/client/back/methods/client/specs/updatePortfolio.spec.js b/modules/client/back/methods/client/specs/updatePortfolio.spec.js deleted file mode 100644 index bf681eb2e..000000000 --- a/modules/client/back/methods/client/specs/updatePortfolio.spec.js +++ /dev/null @@ -1,75 +0,0 @@ -const models = require('vn-loopback/server/server').models; -const LoopBackContext = require('loopback-context'); - -describe('Client updatePortfolio', () => { - const activeCtx = { - accessToken: {userId: 9}, - http: { - req: { - headers: {origin: 'http://localhost'}, - [`__`]: value => { - return value; - } - } - } - }; - - beforeAll(() => { - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ - active: activeCtx - }); - }); - - it('should update the portfolioWeight when the salesPerson of a client changes', async() => { - const clientId = 1108; - const salesPersonId = 18; - - const tx = await models.Client.beginTransaction({}); - - try { - const options = {transaction: tx}; - const expectedResult = 841.63; - - const client = await models.Client.findById(clientId, null, options); - await client.updateAttribute('salesPersonFk', salesPersonId, options); - - await models.Client.updatePortfolio(options); - - const portfolioQuery = `SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `; - const [salesPerson] = await models.Client.rawSql(portfolioQuery, null, options); - - expect(salesPerson.portfolioWeight).toEqual(expectedResult); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } - }); - - it('should keep the same portfolioWeight when a salesperson is unassigned of a client', async() => { - const clientId = 1107; - const salesPersonId = 19; - const tx = await models.Client.beginTransaction({}); - - try { - const options = {transaction: tx}; - const expectedResult = 34.40; - - const client = await models.Client.findById(clientId, null, options); - await client.updateAttribute('salesPersonFk', null, options); - - await models.Client.updatePortfolio(); - - const portfolioQuery = `SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `; - const [salesPerson] = await models.Client.rawSql(portfolioQuery); - - expect(salesPerson.portfolioWeight).toEqual(expectedResult); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } - }); -}); diff --git a/modules/client/back/methods/client/updatePortfolio.js b/modules/client/back/methods/client/updatePortfolio.js deleted file mode 100644 index 809a84636..000000000 --- a/modules/client/back/methods/client/updatePortfolio.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = function(Self) { - Self.remoteMethodCtx('updatePortfolio', { - description: 'Update salesPeson potfolio weight', - accessType: 'READ', - accepts: [], - returns: { - type: 'Object', - root: true - }, - http: { - path: `/updatePortfolio`, - verb: 'GET' - } - }); - - Self.updatePortfolio = async options => { - const myOptions = {}; - - if (typeof options == 'object') - Object.assign(myOptions, options); - - query = `CALL bs.salesPerson_updatePortfolio()`; - return Self.rawSql(query, null, myOptions); - }; -}; diff --git a/modules/client/back/models/client-methods.js b/modules/client/back/models/client-methods.js index 5134e3942..4b20a822c 100644 --- a/modules/client/back/models/client-methods.js +++ b/modules/client/back/models/client-methods.js @@ -22,7 +22,6 @@ module.exports = Self => { require('../methods/client/summary')(Self); require('../methods/client/updateAddress')(Self); require('../methods/client/updateFiscalData')(Self); - require('../methods/client/updatePortfolio')(Self); require('../methods/client/updateUser')(Self); require('../methods/client/uploadFile')(Self); require('../methods/client/campaignMetricsPdf')(Self); diff --git a/modules/client/front/basic-data/index.js b/modules/client/front/basic-data/index.js index 418663952..b08d642d1 100644 --- a/modules/client/front/basic-data/index.js +++ b/modules/client/front/basic-data/index.js @@ -10,8 +10,6 @@ export default class Controller extends Section { onSubmit() { return this.$.watcher.submit().then(() => { - const query = `Clients/updatePortfolio`; - this.$http.get(query); this.$http.get(`Clients/${this.$params.id}/checkDuplicatedData`); }); } diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js index 803338ef3..ee3310368 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/createPdf.spec.js @@ -11,6 +11,7 @@ describe('InvoiceOut createPdf()', () => { const ctx = {req: activeCtx}; it('should create a new PDF file and set true the hasPdf property', async() => { + pending('https://redmine.verdnatura.es/issues/4875'); const invoiceId = 1; spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ active: activeCtx diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js index 08f049783..536fa07a0 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js @@ -30,6 +30,7 @@ describe('InvoiceOut downloadZip()', () => { }); it('should return an error if the size of the files is too large', async() => { + pending('https://redmine.verdnatura.es/issues/4875'); const tx = await models.InvoiceOut.beginTransaction({}); let error; diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js index 02f982011..7b5886236 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/filter.spec.js @@ -51,6 +51,7 @@ describe('InvoiceOut filter()', () => { }); it('should return the invoice out matching hasPdf', async() => { + pending('https://redmine.verdnatura.es/issues/4875'); const tx = await models.InvoiceOut.beginTransaction({}); const options = {transaction: tx}; diff --git a/modules/route/back/methods/route/getTickets.js b/modules/route/back/methods/route/getTickets.js index 18e5abaf2..708644c1a 100644 --- a/modules/route/back/methods/route/getTickets.js +++ b/modules/route/back/methods/route/getTickets.js @@ -33,36 +33,36 @@ module.exports = Self => { const stmt = new ParameterizedSQL( `SELECT - t.id, - t.packages, - t.warehouseFk, - t.nickname, - t.clientFk, - t.priority, - t.addressFk, - st.code AS ticketStateCode, - st.name AS ticketStateName, - wh.name AS warehouseName, - tob.description AS ticketObservation, - a.street, - a.postalCode, - a.city, - am.name AS agencyModeName, - u.nickname AS userNickname, - vn.ticketTotalVolume(t.id) AS volume, - tob.description - FROM route r - JOIN ticket t ON t.routeFk = r.id - LEFT JOIN ticketState ts ON ts.ticketFk = t.id - LEFT JOIN state st ON st.id = ts.stateFk - LEFT JOIN warehouse wh ON wh.id = t.warehouseFk - LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id - LEFT JOIN observationType ot ON tob.observationTypeFk = ot.id - AND ot.code = 'delivery' - LEFT JOIN address a ON a.id = t.addressFk - LEFT JOIN agencyMode am ON am.id = t.agencyModeFk - LEFT JOIN account.user u ON u.id = r.workerFk - LEFT JOIN vehicle v ON v.id = r.vehicleFk` + t.id, + t.packages, + t.warehouseFk, + t.nickname, + t.clientFk, + t.priority, + t.addressFk, + st.code AS ticketStateCode, + st.name AS ticketStateName, + wh.name AS warehouseName, + tob.description AS ticketObservation, + a.street, + a.postalCode, + a.city, + am.name AS agencyModeName, + u.nickname AS userNickname, + vn.ticketTotalVolume(t.id) AS volume, + tob.description + FROM vn.route r + JOIN ticket t ON t.routeFk = r.id + LEFT JOIN ticketState ts ON ts.ticketFk = t.id + LEFT JOIN state st ON st.id = ts.stateFk + LEFT JOIN warehouse wh ON wh.id = t.warehouseFk + LEFT JOIN observationType ot ON ot.code = 'delivery' + LEFT JOIN ticketObservation tob ON tob.ticketFk = t.id + AND tob.observationTypeFk = ot.id + LEFT JOIN address a ON a.id = t.addressFk + LEFT JOIN agencyMode am ON am.id = t.agencyModeFk + LEFT JOIN account.user u ON u.id = r.workerFk + LEFT JOIN vehicle v ON v.id = r.vehicleFk` ); if (!filter.where) filter.where = {}; diff --git a/modules/travel/front/extra-community/index.html b/modules/travel/front/extra-community/index.html index 5174f8da2..ee8dcdf98 100644 --- a/modules/travel/front/extra-community/index.html +++ b/modules/travel/front/extra-community/index.html @@ -27,7 +27,7 @@
diff --git a/modules/travel/front/extra-community/index.js b/modules/travel/front/extra-community/index.js index a4ac487e6..2389570b9 100644 --- a/modules/travel/front/extra-community/index.js +++ b/modules/travel/front/extra-community/index.js @@ -43,16 +43,6 @@ class Controller extends Section { this.smartTableOptions = {}; } - get hasDateRange() { - const userParams = this.$.model.userParams; - const hasLanded = userParams.landedTo; - const hasShipped = userParams.shippedFrom; - const hasContinent = userParams.continent; - const hasWarehouseOut = userParams.warehouseOutFk; - - return hasLanded || hasShipped || hasContinent || hasWarehouseOut; - } - onDragInterval() { if (this.dragClientY > 0 && this.dragClientY < 75) this.$window.scrollTo(0, this.$window.scrollY - 10); diff --git a/modules/travel/front/extra-community/index.spec.js b/modules/travel/front/extra-community/index.spec.js index ae48b9ca1..18ddee665 100644 --- a/modules/travel/front/extra-community/index.spec.js +++ b/modules/travel/front/extra-community/index.spec.js @@ -14,17 +14,6 @@ describe('Travel Component vnTravelExtraCommunity', () => { controller.$.model.refresh = jest.fn(); })); - describe('hasDateRange()', () => { - it('should return truthy when shippedFrom or landedTo are set as userParams', () => { - const now = new Date(); - controller.$.model.userParams = {shippedFrom: now, landedTo: now}; - - const result = controller.hasDateRange; - - expect(result).toBeTruthy(); - }); - }); - describe('findDraggable()', () => { it('should find the draggable element', () => { const draggable = document.createElement('tr'); diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index cd9cacbd7..b38405c1d 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -332,18 +332,9 @@ module.exports = Self => { }, myOptions); const timestamp = started.getTime() / 1000; - await models.Mail.create({ - receiver: previousReceiver, - subject: $t('Record of hours week', { - week: args.week, - year: args.year - }), - body: `${salix.url}worker/${previousWorkerFk}/time-control?timestamp=${timestamp}` - }, myOptions); + const url = `${salix.url}worker/${previousWorkerFk}/time-control?timestamp=${timestamp}`; - query = `INSERT IGNORE INTO workerTimeControlMail (workerFk, year, week) - VALUES (?, ?, ?);`; - await Self.rawSql(query, [previousWorkerFk, args.year, args.week], myOptions); + await models.WorkerTimeControl.weeklyHourRecordEmail(ctx, previousReceiver, args.week, args.year, url); previousWorkerFk = day.workerFk; previousReceiver = day.receiver; diff --git a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js index 4cc6e54e3..24bfd6904 100644 --- a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js +++ b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js @@ -2,15 +2,12 @@ const models = require('vn-loopback/server/server').models; describe('workerTimeControl sendMail()', () => { const workerId = 18; - const ctx = { - req: { - __: value => { - return value; - } - }, - args: {} - + const activeCtx = { + getLocale: () => { + return 'en'; + } }; + const ctx = {req: activeCtx, args: {}}; it('should fill time control of a worker without records in Journey and with rest', async() => { const tx = await models.WorkerTimeControl.beginTransaction({}); diff --git a/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js new file mode 100644 index 000000000..0cf614e57 --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js @@ -0,0 +1,53 @@ +const {Email} = require('vn-print'); + +module.exports = Self => { + Self.remoteMethodCtx('weeklyHourRecordEmail', { + description: 'Sends the buyer waste email', + accessType: 'WRITE', + accepts: [ + { + arg: 'recipient', + type: 'string', + description: 'The recipient email', + required: true, + }, + { + arg: 'week', + type: 'number', + required: true, + }, + { + arg: 'year', + type: 'number', + required: true + }, + { + arg: 'url', + type: 'string', + required: true + } + ], + returns: { + type: ['object'], + root: true + }, + http: { + path: '/weekly-hour-hecord-email', + verb: 'POST' + } + }); + + Self.weeklyHourRecordEmail = async(ctx, recipient, week, year, url) => { + const params = { + recipient: recipient, + lang: ctx.req.getLocale(), + week: week, + year: year, + url: url + }; + + const email = new Email('weekly-hour-record', params); + + return email.send(); + }; +}; diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js index 9f802511a..7339f5d15 100644 --- a/modules/worker/back/models/worker-time-control.js +++ b/modules/worker/back/models/worker-time-control.js @@ -7,6 +7,7 @@ module.exports = Self => { require('../methods/worker-time-control/updateTimeEntry')(Self); require('../methods/worker-time-control/sendMail')(Self); require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self); + require('../methods/worker-time-control/weeklyHourRecordEmail')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') diff --git a/package-lock.json b/package-lock.json index 07b0a95fd..2ed6cb275 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "jsdom": "^16.7.0", "jszip": "^3.10.0", "ldapjs": "^2.2.0", - "loopback": "^3.26.0", + "loopback": "^3.28.0", "loopback-boot": "3.3.1", "loopback-component-explorer": "^6.5.0", "loopback-component-storage": "3.6.1", @@ -4878,13 +4878,20 @@ "license": "MIT" }, "node_modules/caniuse-lite": { - "version": "1.0.30001299", + "version": "1.0.30001434", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", + "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", "dev": true, - "license": "CC-BY-4.0", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/canonical-json": { "version": "0.0.4", @@ -12881,6 +12888,66 @@ "xmlcreate": "^1.0.1" } }, + "node_modules/jsbarcode": { + "version": "3.11.5", + "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.11.5.tgz", + "integrity": "sha512-zv3KsH51zD00I/LrFzFSM6dst7rDn0vIMzaiZFL7qusTjPZiPtxg3zxetp0RR7obmjTw4f6NyGgbdkBCgZUIrA==", + "bin": { + "auto.js": "bin/barcodes/CODE128/auto.js", + "Barcode.js": "bin/barcodes/Barcode.js", + "barcodes": "bin/barcodes", + "canvas.js": "bin/renderers/canvas.js", + "checksums.js": "bin/barcodes/MSI/checksums.js", + "codabar": "bin/barcodes/codabar", + "CODE128": "bin/barcodes/CODE128", + "CODE128_AUTO.js": "bin/barcodes/CODE128/CODE128_AUTO.js", + "CODE128.js": "bin/barcodes/CODE128/CODE128.js", + "CODE128A.js": "bin/barcodes/CODE128/CODE128A.js", + "CODE128B.js": "bin/barcodes/CODE128/CODE128B.js", + "CODE128C.js": "bin/barcodes/CODE128/CODE128C.js", + "CODE39": "bin/barcodes/CODE39", + "constants.js": "bin/barcodes/ITF/constants.js", + "defaults.js": "bin/options/defaults.js", + "EAN_UPC": "bin/barcodes/EAN_UPC", + "EAN.js": "bin/barcodes/EAN_UPC/EAN.js", + "EAN13.js": "bin/barcodes/EAN_UPC/EAN13.js", + "EAN2.js": "bin/barcodes/EAN_UPC/EAN2.js", + "EAN5.js": "bin/barcodes/EAN_UPC/EAN5.js", + "EAN8.js": "bin/barcodes/EAN_UPC/EAN8.js", + "encoder.js": "bin/barcodes/EAN_UPC/encoder.js", + "ErrorHandler.js": "bin/exceptions/ErrorHandler.js", + "exceptions": "bin/exceptions", + "exceptions.js": "bin/exceptions/exceptions.js", + "fixOptions.js": "bin/help/fixOptions.js", + "GenericBarcode": "bin/barcodes/GenericBarcode", + "getOptionsFromElement.js": "bin/help/getOptionsFromElement.js", + "getRenderProperties.js": "bin/help/getRenderProperties.js", + "help": "bin/help", + "index.js": "bin/renderers/index.js", + "index.tmp.js": "bin/barcodes/index.tmp.js", + "ITF": "bin/barcodes/ITF", + "ITF.js": "bin/barcodes/ITF/ITF.js", + "ITF14.js": "bin/barcodes/ITF/ITF14.js", + "JsBarcode.js": "bin/JsBarcode.js", + "linearizeEncodings.js": "bin/help/linearizeEncodings.js", + "merge.js": "bin/help/merge.js", + "MSI": "bin/barcodes/MSI", + "MSI.js": "bin/barcodes/MSI/MSI.js", + "MSI10.js": "bin/barcodes/MSI/MSI10.js", + "MSI1010.js": "bin/barcodes/MSI/MSI1010.js", + "MSI11.js": "bin/barcodes/MSI/MSI11.js", + "MSI1110.js": "bin/barcodes/MSI/MSI1110.js", + "object.js": "bin/renderers/object.js", + "options": "bin/options", + "optionsFromStrings.js": "bin/help/optionsFromStrings.js", + "pharmacode": "bin/barcodes/pharmacode", + "renderers": "bin/renderers", + "shared.js": "bin/renderers/shared.js", + "svg.js": "bin/renderers/svg.js", + "UPC.js": "bin/barcodes/EAN_UPC/UPC.js", + "UPCE.js": "bin/barcodes/EAN_UPC/UPCE.js" + } + }, "node_modules/jsbn": { "version": "0.1.1", "license": "MIT" @@ -23759,6 +23826,14 @@ "version": "1.0.2", "license": "Apache-2.0" }, + "node_modules/xmldom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz", + "integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "license": "MIT", @@ -23938,6 +24013,7 @@ "fs-extra": "^7.0.1", "intl": "^1.2.5", "js-yaml": "^3.13.1", + "jsbarcode": "^3.11.5", "jsonexport": "^3.2.0", "juice": "^5.2.0", "log4js": "^6.7.0", @@ -23948,7 +24024,8 @@ "strftime": "^0.10.0", "vue": "^2.6.10", "vue-i18n": "^8.15.0", - "vue-server-renderer": "^2.6.10" + "vue-server-renderer": "^2.6.10", + "xmldom": "^0.6.0" } }, "print/node_modules/fs-extra": { @@ -28107,7 +28184,9 @@ "version": "1.0.0" }, "caniuse-lite": { - "version": "1.0.30001299", + "version": "1.0.30001434", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", + "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", "dev": true }, "canonical-json": { @@ -39828,6 +39907,11 @@ "xmlcreate": "^1.0.1" } }, + "jsbarcode": { + "version": "3.11.5", + "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.11.5.tgz", + "integrity": "sha512-zv3KsH51zD00I/LrFzFSM6dst7rDn0vIMzaiZFL7qusTjPZiPtxg3zxetp0RR7obmjTw4f6NyGgbdkBCgZUIrA==" + }, "jsbn": { "version": "0.1.1" }, @@ -57805,6 +57889,7 @@ "fs-extra": "^7.0.1", "intl": "^1.2.5", "js-yaml": "^3.13.1", + "jsbarcode": "^3.11.5", "jsonexport": "^3.2.0", "juice": "^5.2.0", "log4js": "^6.7.0", @@ -57815,7 +57900,8 @@ "strftime": "^0.10.0", "vue": "^2.6.10", "vue-i18n": "^8.15.0", - "vue-server-renderer": "^2.6.10" + "vue-server-renderer": "^2.6.10", + "xmldom": "^0.6.0" }, "dependencies": { "fs-extra": { @@ -59678,6 +59764,11 @@ "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", "integrity": "sha512-Mbe56Dvj00onbnSo9J0qj/XlY5bfN9KidsOnpd5tRCsR3ekB3hyyNU9fGrTdqNT5ZNvv4BsA2TcQlignsZyVcw==" }, + "xmldom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz", + "integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==" + }, "xtend": { <<<<<<< HEAD "version": "1.0.3", diff --git a/print/core/smtp.js b/print/core/smtp.js index a55ba448d..61b115b5a 100644 --- a/print/core/smtp.js +++ b/print/core/smtp.js @@ -8,10 +8,12 @@ module.exports = { this.transporter = nodemailer.createTransport(config.smtp); }, - send(options) { + async send(options) { options.from = `${config.app.senderName} <${config.app.senderEmail}>`; if (process.env.NODE_ENV !== 'production') { + const notProductionError = {message: 'This not production, this email not sended'}; + await this.mailLog(options, notProductionError); if (!config.smtp.auth.user) return Promise.resolve(true); @@ -24,29 +26,35 @@ module.exports = { throw err; }).finally(async() => { - const attachments = []; - if (options.attachments) { - for (let attachment of options.attachments) { - const fileName = attachment.filename; - const filePath = attachment.path; - if (fileName.includes('.png')) continue; - - if (fileName || filePath) - attachments.push(filePath ? filePath : fileName); - } - } - - const fileNames = attachments.join(',\n'); - await db.rawSql(` - INSERT INTO vn.mail (receiver, replyTo, sent, subject, body, attachment, status) - VALUES (?, ?, 1, ?, ?, ?, ?)`, [ - options.to, - options.replyTo, - options.subject, - options.text || options.html, - fileNames, - error && error.message || 'Sent' - ]); + await this.mailLog(options, error); }); + }, + + async mailLog(options, error) { + const attachments = []; + if (options.attachments) { + for (let attachment of options.attachments) { + const fileName = attachment.filename; + const filePath = attachment.path; + if (fileName.includes('.png')) continue; + + if (fileName || filePath) + attachments.push(filePath ? filePath : fileName); + } + } + + const fileNames = attachments.join(',\n'); + + await db.rawSql(` + INSERT INTO vn.mail (receiver, replyTo, sent, subject, body, attachment, status) + VALUES (?, ?, 1, ?, ?, ?, ?)`, [ + options.to, + options.replyTo, + options.subject, + options.text || options.html, + fileNames, + error && error.message || 'Sent' + ]); } + }; diff --git a/print/templates/email/email-verify/assets/css/import.js b/print/templates/email/email-verify/assets/css/import.js new file mode 100644 index 000000000..7360587f7 --- /dev/null +++ b/print/templates/email/email-verify/assets/css/import.js @@ -0,0 +1,13 @@ +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`, + `${__dirname}/style.css`]) + .mergeStyles(); + diff --git a/print/templates/email/email-verify/assets/css/style.css b/print/templates/email/email-verify/assets/css/style.css new file mode 100644 index 000000000..5db85befa --- /dev/null +++ b/print/templates/email/email-verify/assets/css/style.css @@ -0,0 +1,5 @@ +.external-link { + border: 2px dashed #8dba25; + border-radius: 3px; + text-align: center +} \ No newline at end of file diff --git a/print/templates/email/email-verify/email-verify.html b/print/templates/email/email-verify/email-verify.html new file mode 100644 index 000000000..b8a6e263c --- /dev/null +++ b/print/templates/email/email-verify/email-verify.html @@ -0,0 +1,48 @@ + + + + + + {{ $t('subject') }} + + + + + + + + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+

+ {{ $t(`click`) }} + {{ $t('subject') }} +

+
+
+ + +
+
+ +
+
+ +
+
+
+
+ + diff --git a/print/templates/email/email-verify/email-verify.js b/print/templates/email/email-verify/email-verify.js new file mode 100755 index 000000000..7f0b80a13 --- /dev/null +++ b/print/templates/email/email-verify/email-verify.js @@ -0,0 +1,17 @@ +const Component = require(`vn-print/core/component`); +const emailHeader = new Component('email-header'); +const emailFooter = new Component('email-footer'); + +module.exports = { + name: 'email-verify', + components: { + 'email-header': emailHeader.build(), + 'email-footer': emailFooter.build() + }, + props: { + url: { + type: [String], + required: true + } + } +}; diff --git a/print/templates/email/email-verify/locale/en.yml b/print/templates/email/email-verify/locale/en.yml new file mode 100644 index 000000000..0298f53b4 --- /dev/null +++ b/print/templates/email/email-verify/locale/en.yml @@ -0,0 +1,3 @@ +subject: Email Verify +title: Email Verify +click: Click on the following link to verify this email. If you haven't requested this email, just ignore it diff --git a/print/templates/email/email-verify/locale/es.yml b/print/templates/email/email-verify/locale/es.yml new file mode 100644 index 000000000..37bd6ef27 --- /dev/null +++ b/print/templates/email/email-verify/locale/es.yml @@ -0,0 +1,3 @@ +subject: Verificar correo +title: Verificar correo +click: Pulsa en el siguiente link para verificar este correo. Si no has pedido este correo, simplemente ignóralo diff --git a/print/templates/email/recover-password/assets/css/import.js b/print/templates/email/recover-password/assets/css/import.js new file mode 100644 index 000000000..7360587f7 --- /dev/null +++ b/print/templates/email/recover-password/assets/css/import.js @@ -0,0 +1,13 @@ +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`, + `${__dirname}/style.css`]) + .mergeStyles(); + diff --git a/print/templates/email/recover-password/assets/css/style.css b/print/templates/email/recover-password/assets/css/style.css new file mode 100644 index 000000000..5db85befa --- /dev/null +++ b/print/templates/email/recover-password/assets/css/style.css @@ -0,0 +1,5 @@ +.external-link { + border: 2px dashed #8dba25; + border-radius: 3px; + text-align: center +} \ No newline at end of file diff --git a/print/templates/email/recover-password/locale/es.yml b/print/templates/email/recover-password/locale/es.yml new file mode 100644 index 000000000..c72b108ee --- /dev/null +++ b/print/templates/email/recover-password/locale/es.yml @@ -0,0 +1,3 @@ +subject: Recuperar contraseña +title: Recuperar contraseña +Click on the following link to change your password.: Pulsa en el siguiente link para cambiar tu contraseña. diff --git a/print/templates/email/recover-password/recover-password.html b/print/templates/email/recover-password/recover-password.html new file mode 100644 index 000000000..a654b3d5f --- /dev/null +++ b/print/templates/email/recover-password/recover-password.html @@ -0,0 +1,48 @@ + + + + + + {{ $t('subject') }} + + + + + + + + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+

+ {{ $t('Click on the following link to change your password.') }} + {{ $t('subject') }} +

+
+
+ + +
+
+ +
+
+ +
+
+
+
+ + diff --git a/print/templates/email/recover-password/recover-password.js b/print/templates/email/recover-password/recover-password.js new file mode 100755 index 000000000..b589411a9 --- /dev/null +++ b/print/templates/email/recover-password/recover-password.js @@ -0,0 +1,17 @@ +const Component = require(`vn-print/core/component`); +const emailHeader = new Component('email-header'); +const emailFooter = new Component('email-footer'); + +module.exports = { + name: 'recover-password', + components: { + 'email-header': emailHeader.build(), + 'email-footer': emailFooter.build() + }, + props: { + url: { + type: [String], + required: true + } + } +}; diff --git a/print/templates/email/weekly-hour-record/assets/css/import.js b/print/templates/email/weekly-hour-record/assets/css/import.js new file mode 100644 index 000000000..1582b82c5 --- /dev/null +++ b/print/templates/email/weekly-hour-record/assets/css/import.js @@ -0,0 +1,12 @@ +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/weekly-hour-record/locale/en.yml b/print/templates/email/weekly-hour-record/locale/en.yml new file mode 100644 index 000000000..817e5451e --- /dev/null +++ b/print/templates/email/weekly-hour-record/locale/en.yml @@ -0,0 +1,6 @@ +subject: Weekly time log +title: Record of hours week {0} year {1} +dear: Dear worker +description: Access the following link:

+ {0}

+ Click 'SATISFIED' if you agree with the hours worked. Otherwise, press 'NOT SATISFIED', detailing the cause of the disagreement. diff --git a/print/templates/email/weekly-hour-record/locale/es.yml b/print/templates/email/weekly-hour-record/locale/es.yml new file mode 100644 index 000000000..b70862f16 --- /dev/null +++ b/print/templates/email/weekly-hour-record/locale/es.yml @@ -0,0 +1,6 @@ +subject: Registro de horas semanal +title: Registro de horas semana {0} año {1} +dear: Estimado trabajador +description: Acceda al siguiente enlace:

+ {0}

+ Pulse 'CONFORME' si esta de acuerdo con las horas trabajadas. En caso contrario pulse 'NO CONFORME', detallando la causa de la disconformidad. diff --git a/print/templates/email/weekly-hour-record/weekly-hour-record.html b/print/templates/email/weekly-hour-record/weekly-hour-record.html new file mode 100644 index 000000000..84abb4c61 --- /dev/null +++ b/print/templates/email/weekly-hour-record/weekly-hour-record.html @@ -0,0 +1,9 @@ + +
+
+

{{ $t('title', [week, year]) }}

+

{{$t('dear')}},

+

+
+
+
diff --git a/print/templates/email/weekly-hour-record/weekly-hour-record.js b/print/templates/email/weekly-hour-record/weekly-hour-record.js new file mode 100755 index 000000000..8fdaea0ce --- /dev/null +++ b/print/templates/email/weekly-hour-record/weekly-hour-record.js @@ -0,0 +1,23 @@ +const Component = require(`vn-print/core/component`); +const emailBody = new Component('email-body'); + +module.exports = { + name: 'weekly-hour-record', + components: { + 'email-body': emailBody.build() + }, + props: { + week: { + type: Number, + required: true + }, + year: { + type: Number, + required: true + }, + url: { + type: String, + required: true + } + } +};