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/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/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 31d9be163..dbf75d666 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -1211,11 +1211,11 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
(104, 500),
(105, 5000);
-INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `transactionTypeSageFk`)
+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, NULL, NULL),
- (2, 'Farmer King', 'The farmer', 4000020002, 1, 'B22222222', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 8),
- (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, 'C33333333', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, NULL, NULL);
+ (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, '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 `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
VALUES
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
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
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 6723e865a..b894815b8 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/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/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/getSummary.spec.js b/modules/supplier/back/methods/supplier/specs/getSummary.spec.js
index 85d16bbda..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(4100000001);
+ 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
new file mode 100644
index 000000000..0eec54926
--- /dev/null
+++ b/modules/supplier/back/methods/supplier/specs/updateFiscalData.spec.js
@@ -0,0 +1,88 @@
+const app = require('vn-loopback/server/server');
+const LoopBackContext = require('loopback-context');
+
+describe('Supplier updateFiscalData', () => {
+ const supplierId = 1;
+ const administrativeId = 5;
+ const employeeId = 1;
+ const defaultData = {
+ name: 'Plants SL',
+ nif: '06089160W',
+ account: '4100000001',
+ 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 activeCtx = {
+ accessToken: {userId: administrativeId},
+ };
+ const ctx = {req: activeCtx};
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
+
+ ctx.args = {
+ name: 'Weapon Dealer',
+ nif: 'A68446004',
+ account: '4000000005',
+ sageTaxTypeFk: 5,
+ sageWithholdingFk: 2,
+ sageTransactionTypeFk: 2,
+ postCode: '46460',
+ city: 'VALENCIA',
+ provinceFk: 2,
+ countryFk: 1,
+ };
+
+ const result = await app.models.Supplier.updateFiscalData(ctx, supplierId);
+
+ expect(result.name).toEqual('Weapon Dealer');
+ expect(result.nif).toEqual('A68446004');
+ 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(1);
+
+ // 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..37c94c266 100644
--- a/modules/supplier/back/models/supplier.js
+++ b/modules/supplier/back/models/supplier.js
@@ -1,4 +1,85 @@
+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'
+ });
+
+ 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'],
+ 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/back/models/supplier.json b/modules/supplier/back/models/supplier.json
index 01cc5b51c..596aad745 100644
--- a/modules/supplier/back/models/supplier.json
+++ b/modules/supplier/back/models/supplier.json
@@ -19,7 +19,7 @@
"type": "String"
},
"account": {
- "type": "Number"
+ "type": "String"
},
"countryFk": {
"type": "Number"
@@ -64,7 +64,7 @@
"type": "Number"
},
"postCode": {
- "type": "Number"
+ "type": "String"
},
"payMethodFk": {
"type": "Number"
@@ -77,7 +77,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..1ea3695d6
--- /dev/null
+++ b/modules/supplier/front/fiscal-data/index.html
@@ -0,0 +1,165 @@
+