From 245b32a47f8244080bb446dfbbfea9161414e209 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 30 Oct 2020 15:11:45 +0100 Subject: [PATCH 1/8] Added section --- back/model-config.json | 3 + back/models/sage-withholding.json | 33 +++ db/dump/dumpedFixtures.sql | 18 ++ db/dump/fixtures.sql | 8 +- .../methods/supplier/specs/getSummary.spec.js | 2 +- .../supplier/specs/updateFiscalData.spec.js | 80 +++++++ .../back/methods/supplier/updateFiscalData.js | 78 +++++++ modules/supplier/back/models/supplier.js | 1 + modules/supplier/back/models/supplier.json | 24 +- modules/supplier/front/fiscal-data/index.html | 177 ++++++++++++++ modules/supplier/front/fiscal-data/index.js | 84 +++++++ .../supplier/front/fiscal-data/index.spec.js | 215 ++++++++++++++++++ .../supplier/front/fiscal-data/locale/es.yml | 3 + modules/supplier/front/index.js | 2 + modules/supplier/front/routes.json | 18 ++ 15 files changed, 738 insertions(+), 8 deletions(-) create mode 100644 back/models/sage-withholding.json create mode 100644 modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js create mode 100644 modules/supplier/back/methods/supplier/updateFiscalData.js create mode 100644 modules/supplier/front/fiscal-data/index.html create mode 100644 modules/supplier/front/fiscal-data/index.js create mode 100644 modules/supplier/front/fiscal-data/index.spec.js create mode 100644 modules/supplier/front/fiscal-data/locale/es.yml diff --git a/back/model-config.json b/back/model-config.json index 7a59aaf9a..bab228cd5 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -56,6 +56,9 @@ "Sip": { "dataSource": "vn" }, + "SageWithholding": { + "dataSource": "vn" + }, "UserConfigView": { "dataSource": "vn" }, diff --git a/back/models/sage-withholding.json b/back/models/sage-withholding.json new file mode 100644 index 000000000..8d93daeae --- /dev/null +++ b/back/models/sage-withholding.json @@ -0,0 +1,33 @@ +{ + "name": "SageWithholding", + "base": "VnModel", + "options": { + "mysql": { + "table": "sage.TiposRetencion" + } + }, + "properties": { + "id": { + "type": "Number", + "id": true, + "description": "Identifier", + "mysql": { + "columnName": "CodigoRetencion" + } + }, + "withholding": { + "type": "string", + "mysql": { + "columnName": "Retencion" + } + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] +} \ No newline at end of file diff --git a/db/dump/dumpedFixtures.sql b/db/dump/dumpedFixtures.sql index 70e5d9b83..c87eab826 100644 --- a/db/dump/dumpedFixtures.sql +++ b/db/dump/dumpedFixtures.sql @@ -604,6 +604,24 @@ INSERT INTO `TiposTransacciones` VALUES (1,'Rég.general/Oper.interiores bienes UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; +-- +-- Dumping data for table `TiposRetencion` +-- + +LOCK TABLES `TiposRetencion` WRITE; +/*!40000 ALTER TABLE `TiposRetencion` DISABLE KEYS */; +INSERT INTO `TiposRetencion` (`CodigoRetencion`, `Retencion`, `PorcentajeRetencion`, `CuentaCargo`, `CuentaAbono`, `ClaveIrpf`, `CuentaCargoANT_`, `CuentaAbonoANT_`, `IdTipoRetencion`) VALUES +(1, 'RETENCION ESTIMACION OBJETIVA', '1.0000000000', '4730000000', '4751000000', NULL, NULL, NULL, '03811652-0F3A-44A1-AE1C-B19624525D7F'), +(2, 'ACTIVIDADES AGRICOLAS O GANADERAS', '2.0000000000', '4730000000', '4751000000', NULL, NULL, NULL, 'F3F91EF3-FED6-444D-B03C-75B639D13FB4'), +(9, 'ACTIVIDADES PROFESIONALES 2 PRIMEROS AÑOS', '9.0000000000', '4730000000', '4751000000', NULL, NULL, NULL, '73F95642-E951-4C91-970A-60C503A4792B'), +(15, 'ACTIVIDADES PROFESIONALES', '15.0000000000', '4730000000', '4751000000', '6', NULL, NULL, 'F6BDE0EE-3B01-4023-8FFF-A73AE9AC50D7'), +(19, 'ARRENDAMIENTO Y SUBARRENDAMIENTO', '19.0000000000', '4730000000', '4751000000', '8', NULL, NULL, '09B033AE-16E5-4057-8D4A-A7710C8A4FB9'); +/*!40000 ALTER TABLE `TiposRetencion` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + + + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index cc3f2f689..ed194e3a8 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1208,11 +1208,11 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`) (104, 500), (105, 5000); -INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`) +INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`) VALUES - (1, 'Plants SL', 'Plants nick', 4000000001, 1, '06089160W', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15), - (2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 10), - (442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15); + (1, 'Plants SL', 'Plants nick', 4000000001, 1, '06089160W', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1), + (2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 10, 5, 2, 2), + (442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, 6, 9, 3); INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`) VALUES diff --git a/modules/supplier/back/methods/supplier/specs/getSummary.spec.js b/modules/supplier/back/methods/supplier/specs/getSummary.spec.js index 42e89afd4..ec2e89c88 100644 --- a/modules/supplier/back/methods/supplier/specs/getSummary.spec.js +++ b/modules/supplier/back/methods/supplier/specs/getSummary.spec.js @@ -7,7 +7,7 @@ describe('Supplier getSummary()', () => { expect(supplier.id).toEqual(1); expect(supplier.name).toEqual('Plants SL'); expect(supplier.nif).toEqual('06089160W'); - expect(supplier.account).toEqual(4000000001); + expect(supplier.account).toEqual('4000000001'); expect(supplier.payDay).toEqual(15); }); diff --git a/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js new file mode 100644 index 000000000..eac9fe899 --- /dev/null +++ b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js @@ -0,0 +1,80 @@ +const app = require('vn-loopback/server/server'); + +describe('Supplier updateFiscalData', () => { + const supplierId = 1; + const administrativeId = 5; + const employeeId = 1; + const defaultData = { + name: 'Plants SL', + nif: '06089160W', + account: '4000000001', + sageTaxTypeFk: 4, + sageWithholdingFk: 1, + sageTransactionTypeFk: 1, + postCode: '15214', + city: 'PONTEVEDRA', + provinceFk: 1, + countryFk: 1, + }; + + it('should return an error if the user is not administrative', async() => { + const ctx = {req: {accessToken: {userId: employeeId}}}; + ctx.args = {}; + + let error; + await app.models.Supplier.updateFiscalData(ctx, supplierId) + .catch(e => { + error = e; + }); + + expect(error.message).toBeDefined(); + }); + + it('should check that the supplier fiscal data is untainted', async() => { + const supplier = await app.models.Supplier.findById(supplierId); + + expect(supplier.name).toEqual(defaultData.name); + expect(supplier.nif).toEqual(defaultData.nif); + expect(supplier.account).toEqual(defaultData.account); + expect(supplier.sageTaxTypeFk).toEqual(defaultData.sageTaxTypeFk); + expect(supplier.sageWithholdingFk).toEqual(defaultData.sageWithholdingFk); + expect(supplier.sageTransactionTypeFk).toEqual(defaultData.sageTransactionTypeFk); + expect(supplier.postCode).toEqual(defaultData.postCode); + expect(supplier.city).toEqual(defaultData.city); + expect(supplier.provinceFk).toEqual(defaultData.provinceFk); + expect(supplier.countryFk).toEqual(defaultData.countryFk); + }); + + it('should update the supplier fiscal data and return the count if changes made', async() => { + const ctx = {req: {accessToken: {userId: administrativeId}}}; + ctx.args = { + name: 'Weapon Dealer', + nif: 'B11111111', + account: '4000000005', + sageTaxTypeFk: 5, + sageWithholdingFk: 2, + sageTransactionTypeFk: 2, + postCode: '46460', + city: 'VALENCIA', + provinceFk: 2, + countryFk: 2, + }; + + const result = await app.models.Supplier.updateFiscalData(ctx, supplierId); + + expect(result.name).toEqual('Weapon Dealer'); + expect(result.nif).toEqual('B11111111'); + expect(result.account).toEqual('4000000005'); + expect(result.sageTaxTypeFk).toEqual(5); + expect(result.sageWithholdingFk).toEqual(2); + expect(result.sageTransactionTypeFk).toEqual(2); + expect(result.postCode).toEqual('46460'); + expect(result.city).toEqual('VALENCIA'); + expect(result.provinceFk).toEqual(2); + expect(result.countryFk).toEqual(2); + + // Restores + ctx.args = defaultData; + await app.models.Supplier.updateFiscalData(ctx, supplierId); + }); +}); diff --git a/modules/supplier/back/methods/supplier/updateFiscalData.js b/modules/supplier/back/methods/supplier/updateFiscalData.js new file mode 100644 index 000000000..be031a18a --- /dev/null +++ b/modules/supplier/back/methods/supplier/updateFiscalData.js @@ -0,0 +1,78 @@ +module.exports = Self => { + Self.remoteMethod('updateFiscalData', { + description: 'Updates fiscal data of a supplier', + accessType: 'WRITE', + accepts: [{ + arg: 'ctx', + type: 'Object', + http: {source: 'context'} + }, + { + arg: 'id', + type: 'Number', + description: 'The supplier id', + http: {source: 'path'} + }, + { + arg: 'name', + type: 'string' + }, + { + arg: 'nif', + type: 'string' + }, + { + arg: 'account', + type: 'string' + }, + { + arg: 'sageTaxTypeFk', + type: 'number' + }, + { + arg: 'sageWithholdingFk', + type: 'number' + }, + { + arg: 'sageTransactionTypeFk', + type: 'number' + }, + { + arg: 'postCode', + type: 'string' + }, + { + arg: 'city', + type: 'string' + }, + { + arg: 'provinceFk', + type: 'number' + }, + { + arg: 'countryFk', + type: 'number' + }], + returns: { + arg: 'res', + type: 'string', + root: true + }, + http: { + path: `/:id/updateFiscalData`, + verb: 'PATCH' + } + }); + + Self.updateFiscalData = async(ctx, supplierId) => { + const models = Self.app.models; + const args = ctx.args; + const supplier = await models.Supplier.findById(supplierId); + + // Remove unwanted properties + delete args.ctx; + delete args.id; + + return supplier.updateAttributes(args); + }; +}; diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index d3c32b814..d042037e7 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -1,4 +1,5 @@ module.exports = Self => { require('../methods/supplier/filter')(Self); require('../methods/supplier/getSummary')(Self); + require('../methods/supplier/updateFiscalData')(Self); }; diff --git a/modules/supplier/back/models/supplier.json b/modules/supplier/back/models/supplier.json index b4f5b5318..f0bcd7df8 100644 --- a/modules/supplier/back/models/supplier.json +++ b/modules/supplier/back/models/supplier.json @@ -16,7 +16,7 @@ "type": "String" }, "account": { - "type": "Number" + "type": "String" }, "countryFk": { "type": "Number" @@ -61,7 +61,7 @@ "type": "Number" }, "postCode": { - "type": "Number" + "type": "String" }, "payMethodFk": { "type": "Number" @@ -74,7 +74,25 @@ }, "nickname": { "type": "String" - } + }, + "sageTaxTypeFk": { + "type": "number", + "mysql": { + "columnName": "taxTypeSageFk" + } + }, + "sageTransactionTypeFk": { + "type": "number", + "mysql": { + "columnName": "transactionTypeSageFk" + } + }, + "sageWithholdingFk": { + "type": "number", + "mysql": { + "columnName": "withholdingSageFk" + } + } }, "relations": { "payMethod": { diff --git a/modules/supplier/front/fiscal-data/index.html b/modules/supplier/front/fiscal-data/index.html new file mode 100644 index 000000000..32fd38ad4 --- /dev/null +++ b/modules/supplier/front/fiscal-data/index.html @@ -0,0 +1,177 @@ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + {{code}} - {{town.name}} ({{town.province.name}}, + {{town.province.country.country}}) + + + + + + + + + {{name}}, {{province.name}} + ({{province.country.country}}) + + + + + + {{name}} ({{country.country}}) + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/modules/supplier/front/fiscal-data/index.js b/modules/supplier/front/fiscal-data/index.js new file mode 100644 index 000000000..56588247b --- /dev/null +++ b/modules/supplier/front/fiscal-data/index.js @@ -0,0 +1,84 @@ +import ngModule from '../module'; +import Section from 'salix/components/section'; + +export default class Controller extends Section { + get province() { + return this._province; + } + + // Province auto complete + set province(selection) { + this._province = selection; + + if (!selection) return; + + const country = selection.country; + + if (!this.supplier.countryFk) + this.supplier.countryFk = country.id; + } + + get town() { + return this._town; + } + + // Town auto complete + set town(selection) { + this._town = selection; + + if (!selection) return; + + const province = selection.province; + const country = province.country; + const postcodes = selection.postcodes; + + if (!this.supplier.provinceFk) + this.supplier.provinceFk = province.id; + + if (!this.supplier.countryFk) + this.supplier.countryFk = country.id; + + if (postcodes.length === 1) + this.supplier.postCode = postcodes[0].code; + } + + get postcode() { + return this._postcode; + } + + // Postcode auto complete + set postcode(selection) { + const oldValue = this._postcode; + this._postcode = selection; + + if (!selection || !oldValue) return; + + const town = selection.town; + const province = town.province; + const country = province.country; + + if (!this.supplier.city) + this.supplier.city = town.name; + + if (!this.supplier.provinceFk) + this.supplier.provinceFk = province.id; + + if (!this.supplier.countryFk) + this.supplier.countryFk = country.id; + } + + onResponse(response) { + this.supplier.postCode = response.code; + this.supplier.city = response.city; + this.supplier.provinceFk = response.provinceFk; + this.supplier.countryFk = response.countryFk; + } +} + +ngModule.vnComponent('vnSupplierFiscalData', { + template: require('./index.html'), + controller: Controller, + bindings: { + supplier: '<' + } +}); diff --git a/modules/supplier/front/fiscal-data/index.spec.js b/modules/supplier/front/fiscal-data/index.spec.js new file mode 100644 index 000000000..b96de89ac --- /dev/null +++ b/modules/supplier/front/fiscal-data/index.spec.js @@ -0,0 +1,215 @@ +import './index'; +import watcher from 'core/mocks/watcher'; + +describe('Supplier', () => { + describe('Component vnSupplierFiscalData', () => { + let $httpBackend; + let $scope; + let $element; + let controller; + + beforeEach(ngModule('supplier')); + + beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + $scope = $rootScope.$new(); + $scope.watcher = watcher; + $scope.watcher.orgData = {id: 101, isEqualizated: false, isTaxDataChecked: false}; + $element = angular.element(''); + controller = $componentController('vnClientFiscalData', {$element, $scope}); + controller.card = {reload: () => {}}; + controller.client = { + id: 101, + email: 'batman@gothamcity.com', + phone: '1111111111', + isEqualizated: false, + isTaxDataChecked: false + }; + + controller._province = {}; + controller._town = {}; + controller._postcode = {}; + })); + + describe('onSubmit()', () => { + it('should call the save() method directly', () => { + jest.spyOn(controller, 'save'); + + controller.onSubmit(); + + expect(controller.save).toHaveBeenCalledWith(); + }); + + it('should call the checkExistingClient() if the isTaxDataChecked property is checked', () => { + jest.spyOn(controller, 'save'); + jest.spyOn(controller, 'checkExistingClient'); + + controller.client.isTaxDataChecked = true; + controller.onSubmit(); + + expect(controller.save).not.toHaveBeenCalledWith(); + expect(controller.checkExistingClient).toHaveBeenCalledWith(); + }); + }); + + describe('checkExistingClient()', () => { + it(`should make a HTTP GET query filtering by email, phone and mobile`, () => { + controller.client.mobile = 222222222; + const filterObj = { + where: { + and: [ + {or: [ + {email: controller.client.email}, + {phone: controller.client.phone}, + {mobile: controller.client.mobile} + ]}, + {id: {neq: controller.client.id}} + ] + } + }; + const filter = encodeURIComponent(JSON.stringify(filterObj)); + $httpBackend.expect('GET', `Clients/findOne?filter=${filter}`).respond(404); + controller.checkExistingClient(); + $httpBackend.flush(); + }); + + it(`should show a save confirmation and then set the despiteOfClient property`, () => { + controller.$.confirmDuplicatedClient = {show: () => {}}; + jest.spyOn(controller.$.confirmDuplicatedClient, 'show'); + + const filterObj = { + where: { + and: [ + {or: [{email: controller.client.email}, {phone: controller.client.phone}]}, + {id: {neq: controller.client.id}} + ] + } + }; + const expectedClient = {id: 102}; + const filter = encodeURIComponent(JSON.stringify(filterObj)); + $httpBackend.expect('GET', `Clients/findOne?filter=${filter}`).respond(expectedClient); + controller.checkExistingClient(); + $httpBackend.flush(); + + expect(controller.$.confirmDuplicatedClient.show).toHaveBeenCalledWith(); + expect(controller.client.despiteOfClient).toEqual(102); + }); + }); + + describe('checkEtChanges()', () => { + it(`should show a propagation confirmation if isEqualizated property is changed and invoice by address is checked`, () => { + controller.$.propagateIsEqualizated = {show: () => {}}; + jest.spyOn(controller.$.propagateIsEqualizated, 'show'); + + const orgData = $scope.watcher.orgData; + orgData.hasToInvoiceByAddress = true; + controller.client.isEqualizated = true; + + controller.checkEtChanges(orgData); + + expect(controller.$.propagateIsEqualizated.show).toHaveBeenCalledWith(); + }); + + it(`should call to the onAcceptEt() method if isEqualizated property is changed and invoice by address isn't checked`, () => { + jest.spyOn(controller, 'onAcceptEt'); + + const orgData = $scope.watcher.orgData; + orgData.hasToInvoiceByAddress = false; + controller.client.isEqualizated = true; + + controller.checkEtChanges(orgData); + + expect(controller.onAcceptEt).toHaveBeenCalledWith(); + }); + }); + + describe('onAcceptEt()', () => { + it('should request to patch the propagation of tax status', () => { + controller.client = {id: 123, isEqualizated: false}; + $httpBackend.expectPATCH(`Clients/${controller.client.id}/addressesPropagateRe`, {isEqualizated: controller.client.isEqualizated}).respond('done'); + controller.onAcceptEt(); + $httpBackend.flush(); + }); + }); + + describe('province() setter', () => { + it(`should set countryFk property`, () => { + controller.client.countryFk = null; + controller.province = { + id: 1, + name: 'New york', + country: { + id: 2, + name: 'USA' + } + }; + + expect(controller.client.countryFk).toEqual(2); + }); + }); + + describe('town() setter', () => { + it(`should set provinceFk property`, () => { + controller.town = { + provinceFk: 1, + code: 46001, + province: { + id: 1, + name: 'New york', + country: { + id: 2, + name: 'USA' + } + }, + postcodes: [] + }; + + expect(controller.client.provinceFk).toEqual(1); + }); + + it(`should set provinceFk property and fill the postalCode if there's just one`, () => { + controller.town = { + provinceFk: 1, + code: 46001, + province: { + id: 1, + name: 'New york', + country: { + id: 2, + name: 'USA' + } + }, + postcodes: [{code: '46001'}] + }; + + expect(controller.client.provinceFk).toEqual(1); + expect(controller.client.postcode).toEqual('46001'); + }); + }); + + describe('postcode() setter', () => { + it(`should set the town, provinceFk and contryFk properties`, () => { + controller.postcode = { + townFk: 1, + code: 46001, + town: { + id: 1, + name: 'New York', + province: { + id: 1, + name: 'New york', + country: { + id: 2, + name: 'USA' + } + } + } + }; + + expect(controller.client.city).toEqual('New York'); + expect(controller.client.provinceFk).toEqual(1); + expect(controller.client.countryFk).toEqual(2); + }); + }); + }); +}); diff --git a/modules/supplier/front/fiscal-data/locale/es.yml b/modules/supplier/front/fiscal-data/locale/es.yml new file mode 100644 index 000000000..8b98a91af --- /dev/null +++ b/modules/supplier/front/fiscal-data/locale/es.yml @@ -0,0 +1,3 @@ +Sage tax type: Tipo de impuesto Sage +Sage transaction type: Tipo de transacción Sage +Sage withholding: Retención Sage diff --git a/modules/supplier/front/index.js b/modules/supplier/front/index.js index daf19e606..46db8774d 100644 --- a/modules/supplier/front/index.js +++ b/modules/supplier/front/index.js @@ -6,3 +6,5 @@ import './search-panel'; import './summary'; import './card'; import './descriptor'; +import './fiscal-data'; + diff --git a/modules/supplier/front/routes.json b/modules/supplier/front/routes.json index 78ae46985..2e32ad985 100644 --- a/modules/supplier/front/routes.json +++ b/modules/supplier/front/routes.json @@ -9,6 +9,8 @@ {"state": "supplier.index", "icon": "icon-supplier"} ], "card": [ + {"state": "supplier.card.basicData", "icon": "settings"}, + {"state": "supplier.card.fiscalData", "icon": "account_balance"} ] }, "routes": [ @@ -36,6 +38,22 @@ "params": { "supplier": "$ctrl.supplier" } + }, { + "url": "/basic-data", + "state": "supplier.card.basicData", + "component": "vn-supplier-basic-data", + "description": "Basic data", + "params": { + "supplier": "$ctrl.supplier" + } + }, { + "url": "/fiscal-data", + "state": "supplier.card.fiscalData", + "component": "vn-supplier-fiscal-data", + "description": "Fiscal data", + "params": { + "supplier": "$ctrl.supplier" + } } ] } \ No newline at end of file From 9e3186c0df3df0b67ff620c8e1c4cbbaec22b908 Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 2 Nov 2020 08:16:42 +0100 Subject: [PATCH 2/8] Updated unit tests --- back/methods/campaign/spec/upcoming.spec.js | 12 +- .../supplier/front/fiscal-data/index.spec.js | 134 ++---------------- 2 files changed, 18 insertions(+), 128 deletions(-) diff --git a/back/methods/campaign/spec/upcoming.spec.js b/back/methods/campaign/spec/upcoming.spec.js index 953683e7a..14bffe3cf 100644 --- a/back/methods/campaign/spec/upcoming.spec.js +++ b/back/methods/campaign/spec/upcoming.spec.js @@ -2,15 +2,11 @@ const app = require('vn-loopback/server/server'); describe('campaign upcoming()', () => { it('should return the upcoming campaign but from the last year', async() => { - let response = await app.models.Campaign.upcoming(); - - const lastYearDate = new Date(); - lastYearDate.setFullYear(lastYearDate.getFullYear() - 1); - const lastYear = lastYearDate.getFullYear(); - + const response = await app.models.Campaign.upcoming(); const campaignDated = response.dated; - const campaignYear = campaignDated.getFullYear(); + const now = new Date(); - expect(campaignYear).toEqual(lastYear); + expect(campaignDated).toEqual(jasmine.any(Date)); + expect(campaignDated).toBeLessThanOrEqual(now); }); }); diff --git a/modules/supplier/front/fiscal-data/index.spec.js b/modules/supplier/front/fiscal-data/index.spec.js index b96de89ac..6fb135c08 100644 --- a/modules/supplier/front/fiscal-data/index.spec.js +++ b/modules/supplier/front/fiscal-data/index.spec.js @@ -3,27 +3,22 @@ import watcher from 'core/mocks/watcher'; describe('Supplier', () => { describe('Component vnSupplierFiscalData', () => { - let $httpBackend; let $scope; let $element; let controller; beforeEach(ngModule('supplier')); - beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { - $httpBackend = _$httpBackend_; + beforeEach(inject(($componentController, $rootScope) => { $scope = $rootScope.$new(); $scope.watcher = watcher; - $scope.watcher.orgData = {id: 101, isEqualizated: false, isTaxDataChecked: false}; + $scope.watcher.orgData = {id: 1}; $element = angular.element(''); - controller = $componentController('vnClientFiscalData', {$element, $scope}); + controller = $componentController('vnSupplierFiscalData', {$element, $scope}); controller.card = {reload: () => {}}; - controller.client = { - id: 101, - email: 'batman@gothamcity.com', - phone: '1111111111', - isEqualizated: false, - isTaxDataChecked: false + controller.supplier = { + id: 1, + name: 'Batman' }; controller._province = {}; @@ -31,110 +26,9 @@ describe('Supplier', () => { controller._postcode = {}; })); - describe('onSubmit()', () => { - it('should call the save() method directly', () => { - jest.spyOn(controller, 'save'); - - controller.onSubmit(); - - expect(controller.save).toHaveBeenCalledWith(); - }); - - it('should call the checkExistingClient() if the isTaxDataChecked property is checked', () => { - jest.spyOn(controller, 'save'); - jest.spyOn(controller, 'checkExistingClient'); - - controller.client.isTaxDataChecked = true; - controller.onSubmit(); - - expect(controller.save).not.toHaveBeenCalledWith(); - expect(controller.checkExistingClient).toHaveBeenCalledWith(); - }); - }); - - describe('checkExistingClient()', () => { - it(`should make a HTTP GET query filtering by email, phone and mobile`, () => { - controller.client.mobile = 222222222; - const filterObj = { - where: { - and: [ - {or: [ - {email: controller.client.email}, - {phone: controller.client.phone}, - {mobile: controller.client.mobile} - ]}, - {id: {neq: controller.client.id}} - ] - } - }; - const filter = encodeURIComponent(JSON.stringify(filterObj)); - $httpBackend.expect('GET', `Clients/findOne?filter=${filter}`).respond(404); - controller.checkExistingClient(); - $httpBackend.flush(); - }); - - it(`should show a save confirmation and then set the despiteOfClient property`, () => { - controller.$.confirmDuplicatedClient = {show: () => {}}; - jest.spyOn(controller.$.confirmDuplicatedClient, 'show'); - - const filterObj = { - where: { - and: [ - {or: [{email: controller.client.email}, {phone: controller.client.phone}]}, - {id: {neq: controller.client.id}} - ] - } - }; - const expectedClient = {id: 102}; - const filter = encodeURIComponent(JSON.stringify(filterObj)); - $httpBackend.expect('GET', `Clients/findOne?filter=${filter}`).respond(expectedClient); - controller.checkExistingClient(); - $httpBackend.flush(); - - expect(controller.$.confirmDuplicatedClient.show).toHaveBeenCalledWith(); - expect(controller.client.despiteOfClient).toEqual(102); - }); - }); - - describe('checkEtChanges()', () => { - it(`should show a propagation confirmation if isEqualizated property is changed and invoice by address is checked`, () => { - controller.$.propagateIsEqualizated = {show: () => {}}; - jest.spyOn(controller.$.propagateIsEqualizated, 'show'); - - const orgData = $scope.watcher.orgData; - orgData.hasToInvoiceByAddress = true; - controller.client.isEqualizated = true; - - controller.checkEtChanges(orgData); - - expect(controller.$.propagateIsEqualizated.show).toHaveBeenCalledWith(); - }); - - it(`should call to the onAcceptEt() method if isEqualizated property is changed and invoice by address isn't checked`, () => { - jest.spyOn(controller, 'onAcceptEt'); - - const orgData = $scope.watcher.orgData; - orgData.hasToInvoiceByAddress = false; - controller.client.isEqualizated = true; - - controller.checkEtChanges(orgData); - - expect(controller.onAcceptEt).toHaveBeenCalledWith(); - }); - }); - - describe('onAcceptEt()', () => { - it('should request to patch the propagation of tax status', () => { - controller.client = {id: 123, isEqualizated: false}; - $httpBackend.expectPATCH(`Clients/${controller.client.id}/addressesPropagateRe`, {isEqualizated: controller.client.isEqualizated}).respond('done'); - controller.onAcceptEt(); - $httpBackend.flush(); - }); - }); - describe('province() setter', () => { it(`should set countryFk property`, () => { - controller.client.countryFk = null; + controller.supplier.countryFk = null; controller.province = { id: 1, name: 'New york', @@ -144,7 +38,7 @@ describe('Supplier', () => { } }; - expect(controller.client.countryFk).toEqual(2); + expect(controller.supplier.countryFk).toEqual(2); }); }); @@ -164,7 +58,7 @@ describe('Supplier', () => { postcodes: [] }; - expect(controller.client.provinceFk).toEqual(1); + expect(controller.supplier.provinceFk).toEqual(1); }); it(`should set provinceFk property and fill the postalCode if there's just one`, () => { @@ -182,8 +76,8 @@ describe('Supplier', () => { postcodes: [{code: '46001'}] }; - expect(controller.client.provinceFk).toEqual(1); - expect(controller.client.postcode).toEqual('46001'); + expect(controller.supplier.provinceFk).toEqual(1); + expect(controller.supplier.postCode).toEqual('46001'); }); }); @@ -206,9 +100,9 @@ describe('Supplier', () => { } }; - expect(controller.client.city).toEqual('New York'); - expect(controller.client.provinceFk).toEqual(1); - expect(controller.client.countryFk).toEqual(2); + expect(controller.supplier.city).toEqual('New York'); + expect(controller.supplier.provinceFk).toEqual(1); + expect(controller.supplier.countryFk).toEqual(2); }); }); }); From 61e63cdd48669f822bbfbac4eebc99296d02adf0 Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 2 Nov 2020 08:53:28 +0100 Subject: [PATCH 3/8] Updated unit tests --- .../back/methods/supplier/specs/getSummary.spec.js | 2 +- .../methods/supplier/specs/updateFiscalData.spec.js | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/supplier/back/methods/supplier/specs/getSummary.spec.js b/modules/supplier/back/methods/supplier/specs/getSummary.spec.js index ec2e89c88..30713f517 100644 --- a/modules/supplier/back/methods/supplier/specs/getSummary.spec.js +++ b/modules/supplier/back/methods/supplier/specs/getSummary.spec.js @@ -7,7 +7,7 @@ describe('Supplier getSummary()', () => { expect(supplier.id).toEqual(1); expect(supplier.name).toEqual('Plants SL'); expect(supplier.nif).toEqual('06089160W'); - expect(supplier.account).toEqual('4000000001'); + expect(supplier.account).toEqual('4100000001'); expect(supplier.payDay).toEqual(15); }); diff --git a/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js index eac9fe899..84e702914 100644 --- a/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js +++ b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js @@ -1,4 +1,5 @@ const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); describe('Supplier updateFiscalData', () => { const supplierId = 1; @@ -7,7 +8,7 @@ describe('Supplier updateFiscalData', () => { const defaultData = { name: 'Plants SL', nif: '06089160W', - account: '4000000001', + account: '4100000001', sageTaxTypeFk: 4, sageWithholdingFk: 1, sageTransactionTypeFk: 1, @@ -46,7 +47,14 @@ describe('Supplier updateFiscalData', () => { }); it('should update the supplier fiscal data and return the count if changes made', async() => { - const ctx = {req: {accessToken: {userId: administrativeId}}}; + const activeCtx = { + accessToken: {userId: administrativeId}, + }; + const ctx = {req: activeCtx}; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + ctx.args = { name: 'Weapon Dealer', nif: 'B11111111', From 590de622f58629547aca85a1925628ac490980ee Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 2 Nov 2020 14:30:33 +0100 Subject: [PATCH 4/8] Added model validations --- db/changes/10250-curfew/00-ACL.sql | 3 + db/dump/fixtures.sql | 4 +- loopback/locale/es.json | 4 +- .../util}/specs/validateIban.spec.js | 0 .../util}/specs/validateTin.spec.js | 0 .../util}/validateIban.js | 0 .../util}/validateTin.js | 0 modules/client/back/models/client.js | 13 ++-- modules/client/front/locale/es.yml | 2 +- .../supplier/specs/updateFiscalData.spec.js | 8 +-- modules/supplier/back/models/supplier.js | 59 +++++++++++++++++++ modules/supplier/front/fiscal-data/index.html | 10 +++- modules/supplier/front/index.js | 3 +- modules/supplier/front/routes.json | 3 +- 14 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 db/changes/10250-curfew/00-ACL.sql rename {modules/client/back/validations => loopback/util}/specs/validateIban.spec.js (100%) rename {modules/client/back/validations => loopback/util}/specs/validateTin.spec.js (100%) rename {modules/client/back/validations => loopback/util}/validateIban.js (100%) rename {modules/client/back/validations => loopback/util}/validateTin.js (100%) diff --git a/db/changes/10250-curfew/00-ACL.sql b/db/changes/10250-curfew/00-ACL.sql new file mode 100644 index 000000000..c4987c405 --- /dev/null +++ b/db/changes/10250-curfew/00-ACL.sql @@ -0,0 +1,3 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', 'updateFiscalData', 'WRITE', 'ALLOW', 'ROLE', 'administrative'); +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 907219511..5cfcf92c6 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1211,8 +1211,8 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`) INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`) VALUES (1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1), - (2, 'Farmer King', 'The farmer', 4000020002, 1, 'B22222222', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 2, 8), - (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, 'C33333333', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, 6, 9, 3); + (2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 2, 8), + (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, 6, 9, 3); INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`) VALUES diff --git a/loopback/locale/es.json b/loopback/locale/es.json index c3fe287eb..580bd374f 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -156,5 +156,7 @@ "shipped": "F. envío", "landed": "F. entrega", "addressFk": "Consignatario", - "companyFk": "Empresa" + "companyFk": "Empresa", + "The social name cannot be empty": "La razón social no puede quedar en blanco", + "The nif cannot be empty": "El NIF no puede quedar en blanco" } \ No newline at end of file diff --git a/modules/client/back/validations/specs/validateIban.spec.js b/loopback/util/specs/validateIban.spec.js similarity index 100% rename from modules/client/back/validations/specs/validateIban.spec.js rename to loopback/util/specs/validateIban.spec.js diff --git a/modules/client/back/validations/specs/validateTin.spec.js b/loopback/util/specs/validateTin.spec.js similarity index 100% rename from modules/client/back/validations/specs/validateTin.spec.js rename to loopback/util/specs/validateTin.spec.js diff --git a/modules/client/back/validations/validateIban.js b/loopback/util/validateIban.js similarity index 100% rename from modules/client/back/validations/validateIban.js rename to loopback/util/validateIban.js diff --git a/modules/client/back/validations/validateTin.js b/loopback/util/validateTin.js similarity index 100% rename from modules/client/back/validations/validateTin.js rename to loopback/util/validateTin.js diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index 36bc60dfa..01e5add6d 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -1,7 +1,9 @@ -let request = require('request-promise-native'); -let UserError = require('vn-loopback/util/user-error'); -let getFinalState = require('vn-loopback/util/hook').getFinalState; -let isMultiple = require('vn-loopback/util/hook').isMultiple; +const request = require('request-promise-native'); +const UserError = require('vn-loopback/util/user-error'); +const getFinalState = require('vn-loopback/util/hook').getFinalState; +const isMultiple = require('vn-loopback/util/hook').isMultiple; +const validateTin = require('vn-loopback/util/validateTin'); +const validateIban = require('vn-loopback/util/validateIban'); const LoopBackContext = require('loopback-context'); module.exports = Self => { @@ -63,7 +65,7 @@ module.exports = Self => { Self.validateAsync('iban', ibanNeedsValidation, { message: 'The IBAN does not have the correct format' }); - let validateIban = require('../validations/validateIban'); + async function ibanNeedsValidation(err, done) { let filter = { fields: ['code'], @@ -83,7 +85,6 @@ module.exports = Self => { message: 'Invalid TIN' }); - let validateTin = require('../validations/validateTin'); async function tinIsValid(err, done) { if (!this.isTaxDataChecked) return done(); diff --git a/modules/client/front/locale/es.yml b/modules/client/front/locale/es.yml index e332a0229..166bdbe1b 100644 --- a/modules/client/front/locale/es.yml +++ b/modules/client/front/locale/es.yml @@ -7,7 +7,7 @@ Has to invoice: Factura Notify by email: Notificar vía e-mail Country: País Street: Domicilio fiscal -City: Municipio +City: Ciudad Postcode: Código postal Province: Provincia Address: Consignatario diff --git a/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js index 84e702914..0eec54926 100644 --- a/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js +++ b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js @@ -57,7 +57,7 @@ describe('Supplier updateFiscalData', () => { ctx.args = { name: 'Weapon Dealer', - nif: 'B11111111', + nif: 'A68446004', account: '4000000005', sageTaxTypeFk: 5, sageWithholdingFk: 2, @@ -65,13 +65,13 @@ describe('Supplier updateFiscalData', () => { postCode: '46460', city: 'VALENCIA', provinceFk: 2, - countryFk: 2, + countryFk: 1, }; const result = await app.models.Supplier.updateFiscalData(ctx, supplierId); expect(result.name).toEqual('Weapon Dealer'); - expect(result.nif).toEqual('B11111111'); + expect(result.nif).toEqual('A68446004'); expect(result.account).toEqual('4000000005'); expect(result.sageTaxTypeFk).toEqual(5); expect(result.sageWithholdingFk).toEqual(2); @@ -79,7 +79,7 @@ describe('Supplier updateFiscalData', () => { expect(result.postCode).toEqual('46460'); expect(result.city).toEqual('VALENCIA'); expect(result.provinceFk).toEqual(2); - expect(result.countryFk).toEqual(2); + expect(result.countryFk).toEqual(1); // Restores ctx.args = defaultData; diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index d042037e7..1e5773a6b 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -1,5 +1,64 @@ +const UserError = require('vn-loopback/util/user-error'); +const validateTin = require('vn-loopback/util/validateTin'); + module.exports = Self => { require('../methods/supplier/filter')(Self); require('../methods/supplier/getSummary')(Self); require('../methods/supplier/updateFiscalData')(Self); + + Self.validatesPresenceOf('name', { + message: 'The social name cannot be empty' + }); + + Self.validatesUniquenessOf('name', { + message: 'The supplier name must be unique' + }); + + Self.validatesPresenceOf('city', { + message: 'City cannot be empty' + }); + + Self.validatesPresenceOf('nif', { + message: 'The nif cannot be empty' + }); + + Self.validatesUniquenessOf('nif', { + message: 'TIN must be unique' + }); + + Self.validateAsync('nif', tinIsValid, { + message: 'Invalid TIN' + }); + + async function tinIsValid(err, done) { + const filter = { + fields: ['code'], + where: {id: this.countryFk} + }; + const country = await Self.app.models.Country.findOne(filter); + const code = country ? country.code.toLowerCase() : null; + + if (!this.nif || !validateTin(this.nif, code)) + err(); + done(); + } + + function isAlpha(value) { + const regexp = new RegExp(/^[ñça-zA-Z0-9\s]*$/i); + + return regexp.test(value); + } + + Self.observe('before save', async function(ctx) { + let changes = ctx.data || ctx.instance; + let orgData = ctx.currentInstance; + + const socialName = changes.name || orgData.name; + const hasChanges = orgData && changes; + const socialNameChanged = hasChanges + && orgData.socialName != socialName; + + if ((socialNameChanged) && !isAlpha(socialName)) + throw new UserError('The socialName has an invalid format'); + }); }; diff --git a/modules/supplier/front/fiscal-data/index.html b/modules/supplier/front/fiscal-data/index.html index 32fd38ad4..a36a98441 100644 --- a/modules/supplier/front/fiscal-data/index.html +++ b/modules/supplier/front/fiscal-data/index.html @@ -44,13 +44,15 @@ vn-focus label="Social name" ng-model="$ctrl.supplier.name" - rule - info="You can use letters and spaces"> + info="You can use letters and spaces" + required="true" + rule> @@ -120,7 +122,9 @@ url="Towns/location" fields="['id', 'name', 'provinceFk']" show-field="name" - value-field="name"> + value-field="name" + required="true" + rule> {{name}}, {{province.name}} ({{province.country.country}}) diff --git a/modules/supplier/front/index.js b/modules/supplier/front/index.js index a4017bbd9..5f52bc97f 100644 --- a/modules/supplier/front/index.js +++ b/modules/supplier/front/index.js @@ -5,5 +5,6 @@ import './card'; import './descriptor'; import './index/'; import './search-panel'; -import './log'; import './summary'; +import './fiscal-data'; +import './log'; diff --git a/modules/supplier/front/routes.json b/modules/supplier/front/routes.json index d1f0701a1..a5bc48c9a 100644 --- a/modules/supplier/front/routes.json +++ b/modules/supplier/front/routes.json @@ -54,7 +54,8 @@ "description": "Fiscal data", "params": { "supplier": "$ctrl.supplier" - } + }, + "acl": ["administrative"] }, { "url" : "/log", "state": "supplier.card.log", From fd054550276c2c81962e819d37b1e2121a8a88fb Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 4 Nov 2020 11:37:54 +0100 Subject: [PATCH 5/8] Postcode auto-fill fix --- modules/client/front/address/edit/index.js | 2 +- modules/client/front/fiscal-data/index.js | 2 +- modules/supplier/back/models/supplier.js | 21 +++++++++++++++++++ modules/supplier/front/fiscal-data/index.html | 2 +- modules/supplier/front/fiscal-data/index.js | 2 +- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/modules/client/front/address/edit/index.js b/modules/client/front/address/edit/index.js index d588812fa..30201b880 100644 --- a/modules/client/front/address/edit/index.js +++ b/modules/client/front/address/edit/index.js @@ -52,7 +52,7 @@ export default class Controller extends Section { if (!this.address.provinceFk) this.address.provinceFk = province.id; - if (postcodes.length === 1) + if (!this.address.postalCode && postcodes.length === 1) this.address.postalCode = postcodes[0].code; } diff --git a/modules/client/front/fiscal-data/index.js b/modules/client/front/fiscal-data/index.js index 6aed6e304..65129d3f8 100644 --- a/modules/client/front/fiscal-data/index.js +++ b/modules/client/front/fiscal-data/index.js @@ -128,7 +128,7 @@ export default class Controller extends Section { if (!this.client.countryFk) this.client.countryFk = country.id; - if (postcodes.length === 1) + if (!this.client.postcode && postcodes.length === 1) this.client.postcode = postcodes[0].code; } diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index 1e5773a6b..37c94c266 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -30,6 +30,27 @@ module.exports = Self => { message: 'Invalid TIN' }); + Self.validatesLengthOf('postCode', { + allowNull: true, + allowBlank: true, + min: 3, max: 10 + }); + + Self.validateAsync('postCode', hasValidPostcode, { + message: `The postcode doesn't exist. Please enter a correct one` + }); + + async function hasValidPostcode(err, done) { + if (!this.postcode) + return done(); + + const models = Self.app.models; + const postcode = await models.Postcode.findById(this.postcode); + + if (!postcode) err(); + done(); + } + async function tinIsValid(err, done) { const filter = { fields: ['code'], diff --git a/modules/supplier/front/fiscal-data/index.html b/modules/supplier/front/fiscal-data/index.html index a36a98441..6dda60941 100644 --- a/modules/supplier/front/fiscal-data/index.html +++ b/modules/supplier/front/fiscal-data/index.html @@ -59,7 +59,7 @@ diff --git a/modules/supplier/front/fiscal-data/index.js b/modules/supplier/front/fiscal-data/index.js index 56588247b..f2929c91f 100644 --- a/modules/supplier/front/fiscal-data/index.js +++ b/modules/supplier/front/fiscal-data/index.js @@ -38,7 +38,7 @@ export default class Controller extends Section { if (!this.supplier.countryFk) this.supplier.countryFk = country.id; - if (postcodes.length === 1) + if (!this.supplier.postCode && postcodes.length === 1) this.supplier.postCode = postcodes[0].code; } From 2b6048ca4d015d5b8dc095e2d16d24c42f1f778f Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 4 Nov 2020 12:45:02 +0100 Subject: [PATCH 6/8] Removed comment block --- modules/supplier/front/fiscal-data/index.html | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/modules/supplier/front/fiscal-data/index.html b/modules/supplier/front/fiscal-data/index.html index 6dda60941..1ea3695d6 100644 --- a/modules/supplier/front/fiscal-data/index.html +++ b/modules/supplier/front/fiscal-data/index.html @@ -152,22 +152,6 @@ rule> - From 074e10d4cad74c3710e2d6d53ed338f728b7ae97 Mon Sep 17 00:00:00 2001 From: carlosjr Date: Wed, 4 Nov 2020 15:20:07 +0100 Subject: [PATCH 7/8] translations file was corrupted on merge fix --- loopback/locale/es.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 958c06b6d..5a4752324 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -157,5 +157,7 @@ "landed": "F. entrega", "addressFk": "Consignatario", "companyFk": "Empresa", + "The social name cannot be empty": "La razón social no puede quedar en blanco", + "The nif cannot be empty": "El NIF no puede quedar en blanco", "You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados" } \ No newline at end of file From 840da5e6bc506a72400ca473f7d9b6dd6aee5413 Mon Sep 17 00:00:00 2001 From: carlosjr Date: Wed, 4 Nov 2020 18:14:08 +0100 Subject: [PATCH 8/8] e2e path for supplier.fiscalData --- e2e/helpers/selectors.js | 15 +++ .../01_summary_and_descriptor.spec.js | 2 +- e2e/paths/13-supplier/03_fiscal_data.spec.js | 108 ++++++++++++++++++ loopback/locale/en.json | 4 +- 4 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 e2e/paths/13-supplier/03_fiscal_data.spec.js diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 52e359687..02c749b3c 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -923,5 +923,20 @@ export default { thirdContactNotes: 'vn-supplier-contact div:nth-child(3) vn-textfield[ng-model="contact.observation"]', saveButton: 'vn-supplier-contact button[type="submit"]', thirdContactDeleteButton: 'vn-supplier-contact div:nth-child(3) vn-icon-button[icon="delete"]' + }, + supplierBasicData: { + + }, + supplierFiscalData: { + socialName: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.name"]', + taxNumber: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.nif"]', + account: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.account"]', + sageTaxType: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.sageTaxTypeFk"]', + sageWihholding: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.sageWithholdingFk"]', + postCode: 'vn-supplier-fiscal-data vn-datalist[ng-model="$ctrl.supplier.postCode"]', + city: 'vn-supplier-fiscal-data vn-datalist[ng-model="$ctrl.supplier.city"]', + province: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.provinceFk"]', + country: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.countryFk"]', + saveButton: 'vn-supplier-fiscal-data button[type="submit"]', } }; diff --git a/e2e/paths/13-supplier/01_summary_and_descriptor.spec.js b/e2e/paths/13-supplier/01_summary_and_descriptor.spec.js index 21609fced..591a6116a 100644 --- a/e2e/paths/13-supplier/01_summary_and_descriptor.spec.js +++ b/e2e/paths/13-supplier/01_summary_and_descriptor.spec.js @@ -79,6 +79,6 @@ describe('Supplier summary & descriptor path', () => { }); it(`should check the client button isn't present since this supplier should not be a client`, async() => { - await page.waitForSelector(selectors.supplierDescriptor.clientButton, {hidden: true}); + await page.waitForSelector(selectors.supplierDescriptor.clientButton, {visible: false}); }); }); diff --git a/e2e/paths/13-supplier/03_fiscal_data.spec.js b/e2e/paths/13-supplier/03_fiscal_data.spec.js new file mode 100644 index 000000000..2d1e4fbed --- /dev/null +++ b/e2e/paths/13-supplier/03_fiscal_data.spec.js @@ -0,0 +1,108 @@ +import selectors from '../../helpers/selectors.js'; +import getBrowser from '../../helpers/puppeteer'; + +describe('Supplier fiscal data path', () => { + let browser; + let page; + + beforeAll(async() => { + browser = await getBrowser(); + page = browser.page; + await page.loginAndModule('administrative', 'supplier'); + await page.accessToSearchResult('2'); + await page.accessToSection('supplier.card.fiscalData'); + }); + + afterAll(async() => { + await browser.close(); + }); + + it('should attempt to edit the fiscal data but fail as the tax number is invalid', async() => { + await page.clearInput(selectors.supplierFiscalData.city); + await page.clearInput(selectors.supplierFiscalData.province); + await page.clearInput(selectors.supplierFiscalData.country); + await page.clearInput(selectors.supplierFiscalData.postCode); + await page.write(selectors.supplierFiscalData.city, 'Valencia'); + await page.clearInput(selectors.supplierFiscalData.socialName); + await page.write(selectors.supplierFiscalData.socialName, 'Farmer King SL'); + await page.clearInput(selectors.supplierFiscalData.taxNumber); + await page.write(selectors.supplierFiscalData.taxNumber, 'invalid tax number'); + await page.clearInput(selectors.supplierFiscalData.account); + await page.write(selectors.supplierFiscalData.account, 'edited account number'); + await page.autocompleteSearch(selectors.supplierFiscalData.sageWihholding, 'retencion estimacion objetiva'); + await page.autocompleteSearch(selectors.supplierFiscalData.sageTaxType, 'operaciones no sujetas'); + + await page.waitToClick(selectors.supplierFiscalData.saveButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toBe('Invalid Tax number'); + }); + + it('should save the changes as the tax number is valid this time', async() => { + await page.clearInput(selectors.supplierFiscalData.taxNumber); + await page.write(selectors.supplierFiscalData.taxNumber, '12345678Z'); + + await page.waitToClick(selectors.supplierFiscalData.saveButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toBe('Data saved!'); + }); + + it('should reload the section', async() => { + await page.reloadSection('supplier.card.fiscalData'); + }); + + it('should check the socialName was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.socialName, 'value'); + + expect(result).toEqual('Farmer King SL'); + }); + + it('should check the taxNumber was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.taxNumber, 'value'); + + expect(result).toEqual('12345678Z'); + }); + + it('should check the account was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.account, 'value'); + + expect(result).toEqual('edited account number'); + }); + + it('should check the sageWihholding was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.sageWihholding, 'value'); + + expect(result).toEqual('RETENCION ESTIMACION OBJETIVA'); + }); + + it('should check the sageTaxType was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.sageTaxType, 'value'); + + expect(result).toEqual('Operaciones no sujetas'); + }); + + it('should check the postCode was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.postCode, 'value'); + + expect(result).toEqual('46000'); + }); + + it('should check the city was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.city, 'value'); + + expect(result).toEqual('Valencia'); + }); + + it('should check the province was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.province, 'value'); + + expect(result).toEqual('Province one (España)'); + }); + + it('should check the country was edited', async() => { + const result = await page.waitToGetProperty(selectors.supplierFiscalData.country, 'value'); + + expect(result).toEqual('España'); + }); +}); diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 172da6faf..0081af429 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -82,5 +82,7 @@ "landed": "Landed", "addressFk": "Address", "companyFk": "Company", - "You need to fill sage information before you check verified data": "You need to fill sage information before you check verified data" + "You need to fill sage information before you check verified data": "You need to fill sage information before you check verified data", + "The social name cannot be empty": "The social name cannot be empty", + "The nif cannot be empty": "The nif cannot be empty" } \ No newline at end of file