diff --git a/back/methods/account/set-password.js b/back/methods/account/set-password.js
index fc54b5abe..ab4d3b3fe 100644
--- a/back/methods/account/set-password.js
+++ b/back/methods/account/set-password.js
@@ -1,16 +1,15 @@
-
module.exports = Self => {
Self.remoteMethod('setPassword', {
description: 'Sets the user password',
accepts: [
{
arg: 'id',
- type: 'Number',
+ type: 'number',
description: 'The user id',
http: {source: 'path'}
}, {
arg: 'newPassword',
- type: 'String',
+ type: 'string',
description: 'The new password',
required: true
}
diff --git a/db/changes/10481-june/00-ACL.sql b/db/changes/10481-june/00-ACL.sql
new file mode 100644
index 000000000..3236ff1fd
--- /dev/null
+++ b/db/changes/10481-june/00-ACL.sql
@@ -0,0 +1,4 @@
+INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
+ VALUES
+ ('Client','setPassword','WRITE','ALLOW','ROLE','salesPerson'),
+ ('Client','updateUser','WRITE','ALLOW','ROLE','salesPerson');
diff --git a/db/changes/10481-june/delete.keep b/db/changes/10481-june/delete.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 770911d0e..2cebffe02 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -55,6 +55,7 @@ export default {
setPassword: '.vn-menu [name="setPassword"]',
activateAccount: '.vn-menu [name="enableAccount"]',
activateUser: '.vn-menu [name="activateUser"]',
+ deactivateUser: '.vn-menu [name="deactivateUser"]',
newPassword: 'vn-textfield[ng-model="$ctrl.newPassword"]',
repeatPassword: 'vn-textfield[ng-model="$ctrl.repeatPassword"]',
newRole: 'vn-autocomplete[ng-model="$ctrl.newRole"]',
diff --git a/e2e/paths/02-client/07_edit_web_access.spec.js b/e2e/paths/02-client/07_edit_web_access.spec.js
index bcd476f6b..70ec4b5ea 100644
--- a/e2e/paths/02-client/07_edit_web_access.spec.js
+++ b/e2e/paths/02-client/07_edit_web_access.spec.js
@@ -8,7 +8,7 @@ describe('Client Edit web access path', () => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'client');
- await page.accessToSearchResult('Bruce Banner');
+ await page.accessToSearchResult('1105');
await page.accessToSection('client.card.webAccess');
});
@@ -26,7 +26,7 @@ describe('Client Edit web access path', () => {
it(`should update the name`, async() => {
await page.clearInput(selectors.clientWebAccess.userName);
- await page.write(selectors.clientWebAccess.userName, 'Hulk');
+ await page.write(selectors.clientWebAccess.userName, 'Legion');
await page.waitToClick(selectors.clientWebAccess.saveButton);
const message = await page.waitForSnackbar();
@@ -43,30 +43,30 @@ describe('Client Edit web access path', () => {
it('should confirm web access name have been updated', async() => {
const result = await page.waitToGetProperty(selectors.clientWebAccess.userName, 'value');
- expect(result).toEqual('Hulk');
+ expect(result).toEqual('Legion');
});
it(`should navigate to the log section`, async() => {
await page.accessToSection('client.card.log');
});
- it(`should confirm the last log is showing the updated client name and no modifications on the active checkbox`, async() => {
+ it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => {
let lastModificationPreviousValue = await page
.waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText');
let lastModificationCurrentValue = await page
.waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
- expect(lastModificationPreviousValue).toEqual('name BruceBanner active false');
- expect(lastModificationCurrentValue).toEqual('name Hulk active false');
+ expect(lastModificationPreviousValue).toEqual('name MaxEisenhardt active false');
+ expect(lastModificationCurrentValue).toEqual('name Legion active false');
});
- it(`should confirm the penultimate log is showing the updated avtive field and no modifications on the client name`, async() => {
+ it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => {
let penultimateModificationPreviousValue = await page
.waitToGetProperty(selectors.clientLog.penultimateModificationPreviousValue, 'innerText');
let penultimateModificationCurrentValue = await page
.waitToGetProperty(selectors.clientLog.penultimateModificationCurrentValue, 'innerText');
- expect(penultimateModificationPreviousValue).toEqual('name BruceBanner active true');
- expect(penultimateModificationCurrentValue).toEqual('name BruceBanner active false');
+ expect(penultimateModificationPreviousValue).toEqual('name MaxEisenhardt active true');
+ expect(penultimateModificationCurrentValue).toEqual('name MaxEisenhardt active false');
});
});
diff --git a/e2e/paths/14-account/01_create_and_basic_data.spec.js b/e2e/paths/14-account/01_create_and_basic_data.spec.js
index 4048413ba..0fc657375 100644
--- a/e2e/paths/14-account/01_create_and_basic_data.spec.js
+++ b/e2e/paths/14-account/01_create_and_basic_data.spec.js
@@ -36,8 +36,7 @@ describe('Account create and basic data path', () => {
await page.waitForState('account.card.basicData');
});
- it('should reload the section and check the name is as expected', async() => {
- await page.reloadSection('account.card.basicData');
+ it('should check the name is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.name, 'value');
expect(result).toEqual('Remy');
@@ -103,25 +102,39 @@ describe('Account create and basic data path', () => {
});
});
- // creating the account without the active property set to true seems to be creating an active user anyways
- // describe('activate user', () => {
- // it(`should check the inactive user icon is present in the descriptor`, async() => {
- // await page.waitForSelector(selectors.accountDescriptor.activeUserIcon, {visible: true});
- // });
+ describe('deactivate user', () => {
+ it(`should check the inactive user icon isn't present in the descriptor just yet`, async() => {
+ await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
+ });
- // it('should activate the user using the descriptor menu', async() => {
- // await page.waitToClick(selectors.accountDescriptor.menuButton);
- // await page.waitToClick(selectors.accountDescriptor.activateUser);
- // await page.waitToClick(selectors.accountDescriptor.acceptButton);
- // const message = await page.waitForSnackbar();
+ it('should deactivate the user using the descriptor menu', async() => {
+ await page.waitToClick(selectors.accountDescriptor.menuButton);
+ await page.waitToClick(selectors.accountDescriptor.deactivateUser);
+ await page.waitToClick(selectors.accountDescriptor.acceptButton);
+ const message = await page.waitForSnackbar();
- // expect(message.text).toContain('user enabled?');
- // });
+ expect(message.text).toContain('User deactivated!');
+ });
- // it('should check the inactive user icon is not present anymore', async() => {
- // await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
- // });
- // });
+ it('should check the inactive user icon is now present', async() => {
+ await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 1);
+ });
+ });
+
+ describe('activate user', () => {
+ it('should activate the user using the descriptor menu', async() => {
+ await page.waitToClick(selectors.accountDescriptor.menuButton);
+ await page.waitToClick(selectors.accountDescriptor.activateUser);
+ await page.waitToClick(selectors.accountDescriptor.acceptButton);
+ const message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('User activated!');
+ });
+
+ it('should check the inactive user icon is not present anymore', async() => {
+ await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
+ });
+ });
describe('mail forwarding', () => {
it('should activate the mail forwarding and set the recipent email', async() => {
diff --git a/e2e/paths/14-account/02_alias_create_and_basic_data.spec.js b/e2e/paths/14-account/02_alias_create_and_basic_data.spec.js
index 0514899bc..dd35dd740 100644
--- a/e2e/paths/14-account/02_alias_create_and_basic_data.spec.js
+++ b/e2e/paths/14-account/02_alias_create_and_basic_data.spec.js
@@ -56,7 +56,7 @@ describe('Account Alias create and basic data path', () => {
expect(result).toContain('psykers');
});
- it('should search for the IT alias group then access to the users section then check the role listed is the expected one', async() => {
+ it('should search IT alias then access the user section to check the role listed is the expected one', async() => {
await page.accessToSearchResult('IT');
await page.accessToSection('account.alias.card.users');
const rolesCount = await page.countElement(selectors.accountAliasUsers.anyResult);
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index b7e9b43d3..c9dca734f 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -123,5 +123,6 @@
"The worker has hours recorded that day": "The worker has hours recorded that day",
"isWithoutNegatives": "isWithoutNegatives",
"routeFk": "routeFk",
- "Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data"
+ "Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data",
+ "Can't change the password of another worker": "Can't change the password of another worker"
}
\ No newline at end of file
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index 9e2b8989b..23c2281c3 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -226,5 +226,6 @@
"reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "isWithoutNegatives",
- "routeFk": "routeFk"
+ "routeFk": "routeFk",
+ "Can't change the password of another worker": "No se puede cambiar la contraseƱa de otro trabajador"
}
\ No newline at end of file
diff --git a/modules/client/back/methods/client/setPassword.js b/modules/client/back/methods/client/setPassword.js
new file mode 100644
index 000000000..19675d0e8
--- /dev/null
+++ b/modules/client/back/methods/client/setPassword.js
@@ -0,0 +1,32 @@
+module.exports = Self => {
+ Self.remoteMethod('setPassword', {
+ description: 'Sets the password of a non-worker client',
+ accepts: [
+ {
+ arg: 'id',
+ type: 'number',
+ description: 'The user id',
+ http: {source: 'path'}
+ }, {
+ arg: 'newPassword',
+ type: 'string',
+ description: 'The new password',
+ required: true
+ }
+ ],
+ http: {
+ path: `/:id/setPassword`,
+ verb: 'PATCH'
+ }
+ });
+
+ Self.setPassword = async function(id, newPassword) {
+ const models = Self.app.models;
+
+ const isWorker = await models.Worker.findById(id);
+ if (isWorker)
+ throw new Error(`Can't change the password of another worker`);
+
+ await models.Account.setPassword(id, newPassword);
+ };
+};
diff --git a/modules/client/back/methods/client/specs/setPassword.spec.js b/modules/client/back/methods/client/specs/setPassword.spec.js
new file mode 100644
index 000000000..e0de20249
--- /dev/null
+++ b/modules/client/back/methods/client/specs/setPassword.spec.js
@@ -0,0 +1,27 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('Client setPassword', () => {
+ it('should throw an error the setPassword target is not just a client but a worker', async() => {
+ let error;
+
+ try {
+ await models.Client.setPassword(1106, 'newPass?');
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error.message).toEqual(`Can't change the password of another worker`);
+ });
+
+ it('should change the password of the client', async() => {
+ let error;
+
+ try {
+ await models.Client.setPassword(1101, 't0pl3v3l.p455w0rd!');
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+});
diff --git a/modules/client/back/methods/client/specs/updateUser.spec.js b/modules/client/back/methods/client/specs/updateUser.spec.js
new file mode 100644
index 000000000..4dc969906
--- /dev/null
+++ b/modules/client/back/methods/client/specs/updateUser.spec.js
@@ -0,0 +1,58 @@
+const models = require('vn-loopback/server/server').models;
+const LoopBackContext = require('loopback-context');
+describe('Client updateUser', () => {
+ const employeeId = 1;
+ const activeCtx = {
+ accessToken: {userId: employeeId},
+ http: {
+ req: {
+ headers: {origin: 'http://localhost'}
+ }
+ }
+ };
+ const ctx = {
+ req: {accessToken: {userId: employeeId}},
+ args: {name: 'test', active: true}
+ };
+
+ beforeEach(() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
+ });
+
+ it('should throw an error the target user is not just a client but a worker', async() => {
+ let error;
+ try {
+ const clientID = 1106;
+ await models.Client.updateUser(ctx, clientID);
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error.message).toEqual(`Can't update the user details of another worker`);
+ });
+
+ it('should update the user data', async() => {
+ let error;
+
+ const tx = await models.Client.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+
+ const clientID = 1105;
+ await models.Client.updateUser(ctx, clientID, options);
+ const client = await models.Account.findById(clientID, null, options);
+
+ expect(client.name).toEqual('test');
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+});
diff --git a/modules/client/back/methods/client/updateUser.js b/modules/client/back/methods/client/updateUser.js
new file mode 100644
index 000000000..dd5b9f9fe
--- /dev/null
+++ b/modules/client/back/methods/client/updateUser.js
@@ -0,0 +1,56 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('updateUser', {
+ description: 'Updates the user information',
+ accepts: [
+ {
+ arg: 'id',
+ type: 'number',
+ description: 'The user id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'name',
+ type: 'string',
+ description: 'the user name'
+ },
+ {
+ arg: 'active',
+ type: 'boolean',
+ description: 'whether the user is active or not'
+ },
+ ],
+ http: {
+ path: '/:id/updateUser',
+ verb: 'PATCH'
+ }
+ });
+
+ Self.updateUser = async function(ctx, id, options) {
+ const models = Self.app.models;
+ let tx;
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await models.Account.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ const isWorker = await models.Worker.findById(id, null, myOptions);
+ if (isWorker)
+ throw new Error(`Can't update the user details of another worker`);
+
+ const user = await models.Account.findById(id, null, myOptions);
+
+ await user.updateAttributes(ctx.args, myOptions);
+
+ if (tx) await tx.commit();
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
+ }
+ };
+};
diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js
index 90a9b9e23..746261626 100644
--- a/modules/client/back/models/client.js
+++ b/modules/client/back/models/client.js
@@ -8,30 +8,32 @@ const LoopBackContext = require('loopback-context');
module.exports = Self => {
// Methods
- require('../methods/client/getCard')(Self);
- require('../methods/client/createWithUser')(Self);
- require('../methods/client/hasCustomerRole')(Self);
- require('../methods/client/canCreateTicket')(Self);
- require('../methods/client/isValidClient')(Self);
require('../methods/client/addressesPropagateRe')(Self);
+ require('../methods/client/canBeInvoiced')(Self);
+ require('../methods/client/canCreateTicket')(Self);
+ require('../methods/client/checkDuplicated')(Self);
+ require('../methods/client/confirmTransaction')(Self);
+ require('../methods/client/consumption')(Self);
+ require('../methods/client/createAddress')(Self);
+ require('../methods/client/createReceipt')(Self);
+ require('../methods/client/createWithUser')(Self);
+ require('../methods/client/extendedListFilter')(Self);
+ require('../methods/client/getAverageInvoiced')(Self);
+ require('../methods/client/getCard')(Self);
require('../methods/client/getDebt')(Self);
require('../methods/client/getMana')(Self);
- require('../methods/client/getAverageInvoiced')(Self);
- require('../methods/client/summary')(Self);
- require('../methods/client/updateFiscalData')(Self);
require('../methods/client/getTransactions')(Self);
- require('../methods/client/confirmTransaction')(Self);
- require('../methods/client/canBeInvoiced')(Self);
- require('../methods/client/uploadFile')(Self);
+ require('../methods/client/hasCustomerRole')(Self);
+ require('../methods/client/isValidClient')(Self);
require('../methods/client/lastActiveTickets')(Self);
require('../methods/client/sendSms')(Self);
- require('../methods/client/createAddress')(Self);
+ require('../methods/client/setPassword')(Self);
+ require('../methods/client/summary')(Self);
require('../methods/client/updateAddress')(Self);
- require('../methods/client/consumption')(Self);
- require('../methods/client/createReceipt')(Self);
+ require('../methods/client/updateFiscalData')(Self);
require('../methods/client/updatePortfolio')(Self);
- require('../methods/client/checkDuplicated')(Self);
- require('../methods/client/extendedListFilter')(Self);
+ require('../methods/client/updateUser')(Self);
+ require('../methods/client/uploadFile')(Self);
// Validations
@@ -446,7 +448,7 @@ module.exports = Self => {
const app = require('vn-loopback/server/server');
app.on('started', function() {
- let account = app.models.Account;
+ const account = app.models.Account;
account.observe('before save', async ctx => {
if (ctx.isNewInstance) return;
@@ -454,22 +456,26 @@ module.exports = Self => {
});
account.observe('after save', async ctx => {
- let changes = ctx.data || ctx.instance;
+ const changes = ctx.data || ctx.instance;
if (!ctx.isNewInstance && changes) {
- let oldData = ctx.hookState.oldInstance;
- let hasChanges = oldData.name != changes.name || oldData.active != changes.active;
+ const oldData = ctx.hookState.oldInstance;
+ const hasChanges = oldData.name != changes.name || oldData.active != changes.active;
if (!hasChanges) return;
- let userId = ctx.options.accessToken.userId;
- let logRecord = {
- originFk: oldData.id,
- userFk: userId,
- action: 'update',
- changedModel: 'Account',
- oldInstance: {name: oldData.name, active: oldData.active},
- newInstance: {name: changes.name, active: changes.active}
- };
- await Self.app.models.ClientLog.create(logRecord);
+ const isClient = await Self.app.models.Client.count({id: oldData.id});
+ if (isClient) {
+ const loopBackContext = LoopBackContext.getCurrentContext();
+ const userId = loopBackContext.active.accessToken.userId;
+ const logRecord = {
+ originFk: oldData.id,
+ userFk: userId,
+ action: 'update',
+ changedModel: 'Account',
+ oldInstance: {name: oldData.name, active: oldData.active},
+ newInstance: {name: changes.name, active: changes.active}
+ };
+ await Self.app.models.ClientLog.create(logRecord);
+ }
}
});
});
diff --git a/modules/client/front/web-access/index.html b/modules/client/front/web-access/index.html
index 610497994..c807489d6 100644
--- a/modules/client/front/web-access/index.html
+++ b/modules/client/front/web-access/index.html
@@ -1,11 +1,11 @@