diff --git a/db/changes/10400-christmas/00-payMethod_hasVerified.sql b/db/changes/10400-christmas/00-payMethod_hasVerified.sql new file mode 100644 index 000000000..280a9e097 --- /dev/null +++ b/db/changes/10400-christmas/00-payMethod_hasVerified.sql @@ -0,0 +1 @@ +ALTER TABLE `vn`.`payMethod` ADD hasVerified TINYINT(1) DEFAULT 0 NULL; \ No newline at end of file diff --git a/db/changes/10400-christmas/00-updateDepartment.sql b/db/changes/10400-christmas/00-updateDepartment.sql new file mode 100644 index 000000000..ce46220ca --- /dev/null +++ b/db/changes/10400-christmas/00-updateDepartment.sql @@ -0,0 +1,3 @@ +UPDATE `vn`.`department` + SET `notificationEmail` = 'finanzas@verdnatura.es' + WHERE `name` = 'FINANZAS'; \ No newline at end of file diff --git a/db/changes/10400-christmas/00-updateSupplier.sql b/db/changes/10400-christmas/00-updateSupplier.sql new file mode 100644 index 000000000..49fa2eb15 --- /dev/null +++ b/db/changes/10400-christmas/00-updateSupplier.sql @@ -0,0 +1,2 @@ +UPDATE `vn`.`supplier` + SET isPayMethodChecked = TRUE; \ No newline at end of file diff --git a/db/changes/10400-christmas/01-payment_afterInsert.sql b/db/changes/10400-christmas/01-payment_afterInsert.sql new file mode 100644 index 000000000..f422ebce2 --- /dev/null +++ b/db/changes/10400-christmas/01-payment_afterInsert.sql @@ -0,0 +1,33 @@ +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`%` TRIGGER `vn`.`payment_afterInsert` AFTER INSERT ON `payment` FOR EACH ROW +BEGIN + DECLARE vIsPayMethodChecked BOOLEAN; + DECLARE vEmail VARCHAR(150); + + SELECT isPayMethodChecked INTO vIsPayMethodChecked + FROM supplier + WHERE id = NEW.supplierFk; + + + IF vIsPayMethodChecked = FALSE THEN + + SELECT notificationEmail INTO vEmail + FROM department + WHERE name = 'FINANZAS'; + + CALL mail_insert( + vEmail, + NULL, + 'Pago con método sin verificar', + CONCAT( + 'Se ha realizado el pago ', + NEW.id, + ' al proveedor ', + NEW.supplierFk, + ' con el método de pago sin verificar.' + ) + ); + END IF; +END$$ +DELIMITER ; diff --git a/db/changes/10400-christmas/01-supplier_beforeUpdate.sql b/db/changes/10400-christmas/01-supplier_beforeUpdate.sql new file mode 100644 index 000000000..b3ffccf9c --- /dev/null +++ b/db/changes/10400-christmas/01-supplier_beforeUpdate.sql @@ -0,0 +1,26 @@ +DROP TRIGGER IF EXISTS `vn`.`supplier_beforeUpdate`; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`%` TRIGGER `vn`.`supplier_beforeUpdate` + BEFORE UPDATE ON `vn`.`supplier` FOR EACH ROW +BEGIN + DECLARE vHasChange BOOL DEFAULT FALSE; + DECLARE vPayMethodHasVerified BOOL; + + SELECT hasVerified INTO vPayMethodHasVerified + FROM payMethod + WHERE id = NEW.payMethodFk; + + SET vHasChange = (NEW.payDemFk <> OLD.payDemFk) OR (NEW.payDay <> OLD.payDay); + + IF vPayMethodHasVerified AND !vHasChange THEN + SET vHasChange = (NEW.payMethodFk <> OLD.payMethodFk); + END IF; + + IF vHasChange THEN + SET NEW.isPayMethodChecked = FALSE; + END IF; + +END$$ +DELIMITER ; diff --git a/db/changes/10400-christmas/delete.keep b/db/changes/10400-christmas/delete.keep deleted file mode 100644 index 603d82d74..000000000 --- a/db/changes/10400-christmas/delete.keep +++ /dev/null @@ -1 +0,0 @@ -Delete me! \ No newline at end of file diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 3e99bd39e..1bc5a4fad 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -217,14 +217,14 @@ UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com' UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23; -INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`) +INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`, `hasVerified`) VALUES - (1, NULL, 'PayMethod one', 0, 001, 0, 0), - (2, NULL, 'PayMethod two', 10, 001, 0, 0), - (3, 'compensation', 'PayMethod three', 0, 001, 0, 0), - (4, NULL, 'PayMethod with IBAN', 0, 001, 1, 0), - (5, NULL, 'PayMethod five', 10, 001, 0, 0), - (8,'wireTransfer', 'WireTransfer', 5, 001, 1, 1); + (1, NULL, 'PayMethod one', 0, 001, 0, 0, 0), + (2, NULL, 'PayMethod two', 10, 001, 0, 0, 1), + (3, 'compensation', 'PayMethod three', 0, 001, 0, 0, 0), + (4, NULL, 'PayMethod with IBAN', 0, 001, 1, 0, 0), + (5, NULL, 'PayMethod five', 10, 001, 0, 0, 0), + (8,'wireTransfer', 'WireTransfer', 5, 001, 1, 1, 0); INSERT INTO `vn`.`payDem`(`id`, `payDem`) VALUES diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 24b87b398..8675797e7 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -524,7 +524,7 @@ export default { saturdayButton: '.vn-popup.shown vn-tool-bar > vn-button:nth-child(6)', acceptDialog: '.vn-dialog.shown button[response="accept"]', acceptChangeHourButton: '.vn-dialog.shown button[response="accept"]', - descriptorDeliveryDate: 'vn-ticket-descriptor slot-body > .attributes > vn-label-value:nth-child(3) > section > span', + descriptorDeliveryDate: 'vn-ticket-descriptor slot-body > .attributes > vn-label-value:nth-child(4) > section > span', acceptInvoiceOutButton: '.vn-confirm.shown button[response="accept"]', acceptDeleteStowawayButton: '.vn-dialog.shown button[response="accept"]' }, diff --git a/e2e/paths/13-supplier/02_basic_data.spec.js b/e2e/paths/13-supplier/02_basic_data.spec.js index f98fa779e..4f3c49512 100644 --- a/e2e/paths/13-supplier/02_basic_data.spec.js +++ b/e2e/paths/13-supplier/02_basic_data.spec.js @@ -8,7 +8,7 @@ describe('Supplier basic data path', () => { beforeAll(async() => { browser = await getBrowser(); page = browser.page; - await page.loginAndModule('administrative', 'supplier'); + await page.loginAndModule('financial', 'supplier'); await page.accessToSearchResult('1'); await page.accessToSection('supplier.card.basicData'); }); diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 00c642192..dc662e8d3 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -214,5 +214,6 @@ "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos", "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días", "The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día", - "The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día" + "The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día", + "You can not modify is pay method checked": "No se puede modificar el campo método de pago validado" } \ No newline at end of file diff --git a/modules/supplier/back/models/specs/supplier.spec.js b/modules/supplier/back/models/specs/supplier.spec.js index 27bd873ad..3140981c3 100644 --- a/modules/supplier/back/models/specs/supplier.spec.js +++ b/modules/supplier/back/models/specs/supplier.spec.js @@ -8,6 +8,19 @@ describe('loopback model Supplier', () => { beforeAll(async() => { supplierOne = await models.Supplier.findById(1); supplierTwo = await models.Supplier.findById(442); + + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); }); afterAll(async() => { @@ -32,19 +45,6 @@ describe('loopback model Supplier', () => { }); it('should not throw if the payMethod id is valid', async() => { - const activeCtx = { - accessToken: {userId: 9}, - http: { - req: { - headers: {origin: 'http://localhost'} - } - } - }; - - spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ - active: activeCtx - }); - let error; const supplier = await models.Supplier.findById(442); await supplier.updateAttribute('payMethodFk', 4) @@ -54,5 +54,40 @@ describe('loopback model Supplier', () => { expect(error).not.toBeDefined(); }); + + it('should have checked isPayMethodChecked for payMethod hasVerfified is false', async() => { + const supplier = await models.Supplier.findById(442); + await supplier.updateAttribute('isPayMethodChecked', true); + await supplier.updateAttribute('payMethodFk', 5); + + const result = await models.Supplier.findById(442); + + expect(result.isPayMethodChecked).toEqual(true); + }); + + it('should have unchecked isPayMethodChecked for payMethod hasVerfified is true', async() => { + const supplier = await models.Supplier.findById(442); + await supplier.updateAttribute('isPayMethodChecked', true); + await supplier.updateAttribute('payMethodFk', 2); + + const result = await models.Supplier.findById(442); + + expect(result.isPayMethodChecked).toEqual(false); + }); + + it('should have unchecked isPayMethodChecked for payDay and peyDemFk', async() => { + const supplier = await models.Supplier.findById(442); + + await supplier.updateAttribute('isPayMethodChecked', true); + await supplier.updateAttribute('payDay', 5); + const firstResult = await models.Supplier.findById(442); + + await supplier.updateAttribute('isPayMethodChecked', true); + await supplier.updateAttribute('payDemFk', 1); + const secondResult = await models.Supplier.findById(442); + + expect(firstResult.isPayMethodChecked).toEqual(false); + expect(secondResult.isPayMethodChecked).toEqual(false); + }); }); }); diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index 1ac6e3bd2..2912a3577 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -1,5 +1,6 @@ const UserError = require('vn-loopback/util/user-error'); const validateTin = require('vn-loopback/util/validateTin'); +const LoopBackContext = require('loopback-context'); module.exports = Self => { require('../methods/supplier/filter')(Self); @@ -88,8 +89,24 @@ module.exports = Self => { } Self.observe('before save', async function(ctx) { - let changes = ctx.data || ctx.instance; - let orgData = ctx.currentInstance; + const loopbackContext = LoopBackContext.getCurrentContext(); + const changes = ctx.data || ctx.instance; + const orgData = ctx.currentInstance; + const userId = loopbackContext.active.accessToken.userId; + + const isNotFinancial = !await Self.app.models.Account.hasRole(userId, 'financial'); + const isPayMethodChecked = changes.isPayMethodChecked || orgData.isPayMethodChecked; + const hasChanges = orgData && changes; + const isPayMethodCheckedChanged = hasChanges + && orgData.isPayMethodChecked != isPayMethodChecked; + + if (isNotFinancial && isPayMethodCheckedChanged) + throw new UserError('You can not modify is pay method checked'); + }); + + Self.observe('before save', async function(ctx) { + const changes = ctx.data || ctx.instance; + const orgData = ctx.currentInstance; const socialName = changes.name || orgData.name; const hasChanges = orgData && changes; diff --git a/modules/supplier/front/basic-data/index.html b/modules/supplier/front/basic-data/index.html index 0ec80641d..9991908d4 100644 --- a/modules/supplier/front/basic-data/index.html +++ b/modules/supplier/front/basic-data/index.html @@ -38,7 +38,8 @@ + ng-model="$ctrl.supplier.isPayMethodChecked" + vn-acl="financial"> diff --git a/modules/supplier/front/billing-data/index.js b/modules/supplier/front/billing-data/index.js index 0c9cbb0dc..9d2863f64 100644 --- a/modules/supplier/front/billing-data/index.js +++ b/modules/supplier/front/billing-data/index.js @@ -11,7 +11,8 @@ export default class Controller extends Section { } onSubmit() { - return this.$.watcher.submit(); + this.$.watcher.submit() + .then(() => this.card.reload()); } } @@ -20,5 +21,8 @@ ngModule.vnComponent('vnSupplierBillingData', { controller: Controller, bindings: { supplier: '<' + }, + require: { + card: '^vnSupplierCard' } }); diff --git a/modules/ticket/back/methods/ticket/filter.js b/modules/ticket/back/methods/ticket/filter.js index 44ef7c486..58c440e95 100644 --- a/modules/ticket/back/methods/ticket/filter.js +++ b/modules/ticket/back/methods/ticket/filter.js @@ -83,6 +83,11 @@ module.exports = Self => { type: 'boolean', description: `Whether to show only tickets with problems` }, + { + arg: 'hasRoute', + type: 'boolean', + description: `Whether to show only tickets with route` + }, { arg: 'pending', type: 'boolean', @@ -188,6 +193,10 @@ module.exports = Self => { case 'alertLevel': return {'ts.alertLevel': value}; + case 'hasRoute': + if (value == true) + return {'t.routeFk': {neq: null}}; + return {'t.routeFk': null}; case 'pending': if (value) { return {and: [ @@ -266,7 +275,8 @@ module.exports = Self => { LEFT JOIN state st ON st.id = ts.stateFk LEFT JOIN client c ON c.id = t.clientFk LEFT JOIN worker wk ON wk.id = c.salesPersonFk - LEFT JOIN account.user u ON u.id = wk.userFk`); + LEFT JOIN account.user u ON u.id = wk.userFk + LEFT JOIN route r ON r.id = t.routeFk`); if (args.orderFk) { stmt.merge({ diff --git a/modules/ticket/back/methods/ticket/specs/filter.spec.js b/modules/ticket/back/methods/ticket/specs/filter.spec.js index 14ada5c6e..b251d5335 100644 --- a/modules/ticket/back/methods/ticket/specs/filter.spec.js +++ b/modules/ticket/back/methods/ticket/specs/filter.spec.js @@ -221,4 +221,61 @@ describe('ticket filter()', () => { throw e; } }); + + it('should return the tickets matching the route on true', async() => { + const tx = await models.Ticket.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const ctx = {req: {accessToken: {userId: 9}}, args: {hasRoute: true}}; + const filter = {}; + const result = await models.Ticket.filter(ctx, filter, options); + + expect(result.length).toEqual(22); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should return the tickets matching the route on false', async() => { + const tx = await models.Ticket.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const ctx = {req: {accessToken: {userId: 9}}, args: {hasRoute: false}}; + const filter = {}; + const result = await models.Ticket.filter(ctx, filter, options); + + expect(result.length).toEqual(5); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should return the tickets matching the route on null', async() => { + const tx = await models.Ticket.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const ctx = {req: {accessToken: {userId: 9}}, args: {hasRoute: null}}; + const filter = {}; + const result = await models.Ticket.filter(ctx, filter, options); + + expect(result.length).toEqual(27); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); diff --git a/modules/ticket/front/search-panel/index.html b/modules/ticket/front/search-panel/index.html index 445729952..b0d4963bd 100644 --- a/modules/ticket/front/search-panel/index.html +++ b/modules/ticket/front/search-panel/index.html @@ -135,6 +135,12 @@ ng-model="filter.pending" triple-state="true"> + + diff --git a/modules/ticket/front/search-panel/locale/es.yml b/modules/ticket/front/search-panel/locale/es.yml index d6d01d0aa..52cc04d6e 100644 --- a/modules/ticket/front/search-panel/locale/es.yml +++ b/modules/ticket/front/search-panel/locale/es.yml @@ -12,6 +12,7 @@ Order id: Id cesta Grouped States: Estado agrupado Days onward: Días adelante With problems: Con problemas +Has route: Con ruta Pending: Pendiente FREE: Libre DELIVERED: Servido