diff --git a/e2e/paths/02-client/12_lock_of_verified_data.spec.js b/e2e/paths/02-client/12_lock_of_verified_data.spec.js index 701531c765..af42e2a4bd 100644 --- a/e2e/paths/02-client/12_lock_of_verified_data.spec.js +++ b/e2e/paths/02-client/12_lock_of_verified_data.spec.js @@ -111,7 +111,7 @@ describe('Client lock verified data path', () => { await page.waitToClick(selectors.clientFiscalData.saveButton); const message = await page.waitForSnackbar(); - expect(message.text).toContain(`You can't make changes on a client with verified data`); + expect(message.text).toContain(`Not enough privileges to edit a client with verified data`); }); }); @@ -123,19 +123,19 @@ describe('Client lock verified data path', () => { await page.accessToSection('client.card.fiscalData'); }, 20000); - it('should confirm verified data button is enabled for salesAssistant', async() => { + it('should confirm verified data button is disabled for salesAssistant', async() => { const isDisabled = await page.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox); - expect(isDisabled).toBeFalsy(); + expect(isDisabled).toBeTrue(); }); - it('should now edit the social name', async() => { + it('should return error when edit the social name', async() => { await page.clearInput(selectors.clientFiscalData.socialName); await page.write(selectors.clientFiscalData.socialName, 'new social name edition'); await page.waitToClick(selectors.clientFiscalData.saveButton); const message = await page.waitForSnackbar(); - expect(message.text).toContain('Data saved!'); + expect(message.text).toContain(`Not enough privileges to edit a client with verified data`); }); it('should now confirm the social name have been edited once and for all', async() => { diff --git a/loopback/locale/en.json b/loopback/locale/en.json index b7242befb0..b7e9b43d36 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -12,7 +12,6 @@ "That payment method requires an IBAN": "That payment method requires an IBAN", "That payment method requires a BIC": "That payment method requires a BIC", "The default consignee can not be unchecked": "The default consignee can not be unchecked", - "You can't make changes on a client with verified data": "You can't make changes on a client with verified data", "Enter an integer different to zero": "Enter an integer different to zero", "Package cannot be blank": "Package cannot be blank", "The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one", @@ -123,5 +122,6 @@ "The type of business must be filled in basic data": "The type of business must be filled in basic data", "The worker has hours recorded that day": "The worker has hours recorded that day", "isWithoutNegatives": "isWithoutNegatives", - "routeFk": "routeFk" + "routeFk": "routeFk", + "Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 77bd21780f..a44ba2da81 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -50,7 +50,7 @@ "You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo", "Warehouse cannot be blank": "El almacén no puede quedar en blanco", "Agency cannot be blank": "La agencia no puede quedar en blanco", - "You can't make changes on a client with verified data": "No puedes hacer cambios en un cliente con datos comprobados", + "Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados", "This address doesn't exist": "Este consignatario no existe", "You must delete the claim id %d first": "Antes debes borrar la reclamación %d", "You don't have enough privileges": "No tienes suficientes permisos", diff --git a/modules/client/back/methods/client/specs/addressesPropagateRe.spec.js b/modules/client/back/methods/client/specs/addressesPropagateRe.spec.js index 069c7bd250..74d80b964f 100644 --- a/modules/client/back/methods/client/specs/addressesPropagateRe.spec.js +++ b/modules/client/back/methods/client/specs/addressesPropagateRe.spec.js @@ -1,6 +1,22 @@ const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Client addressesPropagateRe', () => { + beforeAll(async() => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + it('should propagate the isEqualizated on both addresses of Mr Wayne and set hasToInvoiceByAddress to false', async() => { const tx = await models.Client.beginTransaction({}); diff --git a/modules/client/back/methods/client/specs/createAddress.spec.js b/modules/client/back/methods/client/specs/createAddress.spec.js index d6178ae0d8..0841ad98ce 100644 --- a/modules/client/back/methods/client/specs/createAddress.spec.js +++ b/modules/client/back/methods/client/specs/createAddress.spec.js @@ -1,4 +1,5 @@ const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Address createAddress', () => { const clientFk = 1101; @@ -6,6 +7,21 @@ describe('Address createAddress', () => { const incotermsFk = 'FAS'; const customAgentOneId = 1; + beforeAll(async() => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + it('should throw a non uee member error if no incoterms is defined', async() => { const tx = await models.Client.beginTransaction({}); diff --git a/modules/client/back/methods/client/specs/createWithUser.spec.js b/modules/client/back/methods/client/specs/createWithUser.spec.js index 4c827c7453..7d4261aeef 100644 --- a/modules/client/back/methods/client/specs/createWithUser.spec.js +++ b/modules/client/back/methods/client/specs/createWithUser.spec.js @@ -1,4 +1,5 @@ const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Client Create', () => { const newAccount = { @@ -12,6 +13,21 @@ describe('Client Create', () => { businessTypeFk: 'florist' }; + beforeAll(async() => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + it(`should not find Deadpool as he's not created yet`, async() => { const tx = await models.Client.beginTransaction({}); diff --git a/modules/client/back/methods/client/specs/updateAddress.spec.js b/modules/client/back/methods/client/specs/updateAddress.spec.js index 2ad564cc5d..6f02323c5b 100644 --- a/modules/client/back/methods/client/specs/updateAddress.spec.js +++ b/modules/client/back/methods/client/specs/updateAddress.spec.js @@ -1,4 +1,5 @@ const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Address updateAddress', () => { const clientId = 1101; @@ -13,6 +14,21 @@ describe('Address updateAddress', () => { } }; + beforeAll(async() => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + it('should throw the non uee member error if no incoterms is defined', async() => { const tx = await models.Client.beginTransaction({}); @@ -93,6 +109,7 @@ describe('Address updateAddress', () => { try { const options = {transaction: tx}; + ctx.req.accessToken.userId = employeeId; ctx.args = { isLogifloraAllowed: true }; diff --git a/modules/client/back/methods/client/specs/updateFiscalData.spec.js b/modules/client/back/methods/client/specs/updateFiscalData.spec.js index 7c0bc05998..a08f97faf9 100644 --- a/modules/client/back/methods/client/specs/updateFiscalData.spec.js +++ b/modules/client/back/methods/client/specs/updateFiscalData.spec.js @@ -1,10 +1,25 @@ const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Client updateFiscalData', () => { const clientId = 1101; const employeeId = 1; const salesAssistantId = 21; const administrativeId = 5; + const activeCtx = { + accessToken: {userId: employeeId}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); it('should return an error if the user is not salesAssistant and the isTaxDataChecked value is true', async() => { const tx = await models.Client.beginTransaction({}); @@ -24,7 +39,7 @@ describe('Client updateFiscalData', () => { error = e; } - expect(error.message).toEqual(`You can't make changes on a client with verified data`); + expect(error.message).toEqual(`Not enough privileges to edit a client with verified data`); }); it('should return an error if the salesAssistant did not fill the sage data before checking verified data', async() => { diff --git a/modules/client/back/methods/client/updateFiscalData.js b/modules/client/back/methods/client/updateFiscalData.js index 539d89d3a8..c2de8f9279 100644 --- a/modules/client/back/methods/client/updateFiscalData.js +++ b/modules/client/back/methods/client/updateFiscalData.js @@ -125,11 +125,11 @@ module.exports = Self => { } try { - const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions); + const isAdministrative = await models.Account.hasRole(userId, 'administrative', myOptions); const client = await models.Client.findById(clientId, null, myOptions); - if (!isSalesAssistant && client.isTaxDataChecked) - throw new UserError(`You can't make changes on a client with verified data`); + if (!isAdministrative && client.isTaxDataChecked) + throw new UserError(`Not enough privileges to edit a client with verified data`); // Sage data validation const taxDataChecked = args.isTaxDataChecked; diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index 9b4d411c8c..5ed777ab52 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -224,6 +224,34 @@ module.exports = Self => { throw new UserError(`The type of business must be filled in basic data`); }); + Self.observe('before save', async ctx => { + const changes = ctx.data || ctx.instance; + const orgData = ctx.currentInstance; + const models = Self.app.models; + + const loopBackContext = LoopBackContext.getCurrentContext(); + const userId = loopBackContext.active.accessToken.userId; + + const isAdministrative = await models.Account.hasRole(userId, 'administrative', ctx.options); + const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', ctx.options); + const hasChanges = orgData && changes; + + const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked); + const isTaxDataCheckedChanged = hasChanges && orgData.isTaxDataChecked != isTaxDataChecked; + + const sageTaxType = hasChanges && (changes.sageTaxTypeFk || orgData.sageTaxTypeFk); + const sageTaxTypeChanged = hasChanges && orgData.sageTaxTypeFk != sageTaxType; + + const sageTransactionType = hasChanges && (changes.sageTransactionTypeFk || orgData.sageTransactionTypeFk); + const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType; + + const cantEditVerifiedData = isTaxDataCheckedChanged && !isAdministrative; + const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !isSalesAssistant; + + if (cantEditVerifiedData || cantChangeSageData) + throw new UserError(`You don't have enough privileges`); + }); + Self.observe('before save', async function(ctx) { const changes = ctx.data || ctx.instance; const orgData = ctx.currentInstance; @@ -237,10 +265,10 @@ module.exports = Self => { const socialNameChanged = hasChanges && orgData.socialName != socialName; - const dataCheckedChanged = hasChanges + const isTaxDataCheckedChanged = hasChanges && orgData.isTaxDataChecked != isTaxDataChecked; - if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName)) + if ((socialNameChanged || isTaxDataCheckedChanged) && !isAlpha(socialName)) throw new UserError(`The social name has an invalid format`); if (changes.salesPerson === null) { diff --git a/modules/client/back/models/specs/address.spec.js b/modules/client/back/models/specs/address.spec.js index cae18258f4..81af6ee283 100644 --- a/modules/client/back/models/specs/address.spec.js +++ b/modules/client/back/models/specs/address.spec.js @@ -1,20 +1,36 @@ -const app = require('vn-loopback/server/server'); +const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('loopback model address', () => { let createdAddressId; const clientId = 1101; - afterAll(async() => { - let client = await app.models.Client.findById(clientId); + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; - await app.models.Address.destroyById(createdAddressId); + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + afterAll(async() => { + const client = await models.Client.findById(clientId); + + await models.Address.destroyById(createdAddressId); await client.updateAttribute('isEqualizated', false); }); describe('observe()', () => { it('should throw an error when deactivating a consignee if its the default address', async() => { let error; - let address = await app.models.Address.findById(1); + const address = await models.Address.findById(1); await address.updateAttribute('isActive', false) .catch(e => { @@ -27,13 +43,13 @@ describe('loopback model address', () => { }); it('should set isEqualizated to true of a given Client to trigger any new address to have it', async() => { - let client = await app.models.Client.findById(clientId); + const client = await models.Client.findById(clientId); expect(client.isEqualizated).toBeFalsy(); await client.updateAttribute('isEqualizated', true); - let newAddress = await app.models.Address.create({ + const newAddress = await models.Address.create({ clientFk: clientId, agencyModeFk: 5, city: 'here', diff --git a/modules/client/front/fiscal-data/index.html b/modules/client/front/fiscal-data/index.html index f3fd42e760..8556557e3b 100644 --- a/modules/client/front/fiscal-data/index.html +++ b/modules/client/front/fiscal-data/index.html @@ -182,7 +182,7 @@ vn-one label="Verified data" ng-model="$ctrl.client.isTaxDataChecked" - vn-acl="salesAssistant"> + vn-acl="administrative"> diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js index f1c1bbab83..b166caf787 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js @@ -93,6 +93,9 @@ describe('InvoiceOut createManualInvoice()', () => { it('should throw an error for a non-invoiceable client', async() => { spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true))); + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); const tx = await models.InvoiceOut.beginTransaction({}); const options = {transaction: tx};