refactor(client): added restriction to change the credit amount from zero set by a manager #731

Merged
carlosjr merged 2 commits from 3087-client_credit into dev 2021-09-09 14:49:54 +00:00
3 changed files with 112 additions and 48 deletions

View File

@ -207,5 +207,7 @@
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa", "A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes", "Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes", "Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio" "Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente"
} }

View File

@ -217,19 +217,9 @@ module.exports = Self => {
if (isTaxDataCheckedChanged && !orgData.businessTypeFk) if (isTaxDataCheckedChanged && !orgData.businessTypeFk)
throw new UserError(`Can't verify data unless the client has a business type`); throw new UserError(`Can't verify data unless the client has a business type`);
} }
// Credit changes
if (changes.credit !== undefined) { if (changes.credit !== undefined)
await validateCreditChange(ctx, finalState); await Self.changeCredit(ctx, finalState, changes);
let filter = {fields: ['id'], where: {userFk: ctx.options.accessToken.userId}};
let worker = await Self.app.models.Worker.findOne(filter);
let newCredit = {
amount: changes.credit,
clientFk: finalState.id,
workerFk: worker ? worker.id : null
};
await Self.app.models.ClientCredit.create(newCredit);
}
}); });
Self.observe('after save', async ctx => { Self.observe('after save', async ctx => {
@ -332,42 +322,54 @@ module.exports = Self => {
await models.Chat.send(httpCtx, `@${currentWorker.user}`, message); await models.Chat.send(httpCtx, `@${currentWorker.user}`, message);
}; };
async function validateCreditChange(ctx, finalState) { // Credit change validations
let models = Self.app.models; Self.changeCredit = async function changeCredit(ctx, finalState, changes) {
let userId = ctx.options.accessToken.userId; const models = Self.app.models;
const userId = ctx.options.accessToken.userId;
let currentUserIsManager = await models.Account.hasRole(userId, 'manager'); const isManager = await models.Account.hasRole(userId, 'manager', ctx.options);
if (currentUserIsManager) if (!isManager) {
return; const lastCredit = await models.ClientCredit.findOne({
where: {
clientFk: finalState.id
},
order: 'id DESC'
}, ctx.options);
let filter = { const lastAmount = lastCredit && lastCredit.amount;
fields: ['roleFk'], const lastWorkerId = lastCredit && lastCredit.workerFk;
where: { const lastWorkerIsManager = await models.Account.hasRole(lastWorkerId, 'manager', ctx.options);
maxAmount: {gt: ctx.data.credit}
}
};
let limits = await models.ClientCreditLimit.find(filter); if (lastAmount == 0 && lastWorkerIsManager)
throw new UserError(`You can't change the credit set to zero from a manager`);
if (limits.length == 0) const creditLimits = await models.ClientCreditLimit.find({
throw new UserError('Credit limits not found'); fields: ['roleFk'],
where: {
maxAmount: {gte: changes.credit}
}
}, ctx.options);
// Si el usuario no tiene alguno de los roles no continua const requiredRoles = [];
for (limit of creditLimits)
requiredRoles.push(limit.roleFk);
let requiredRoles = []; const userRequiredRoles = await models.RoleMapping.count({
for (limit of limits) roleId: {inq: requiredRoles},
requiredRoles.push(limit.roleFk); principalType: 'USER',
principalId: userId
}, ctx.options);
let where = { if (userRequiredRoles <= 0)
roleId: {inq: requiredRoles}, throw new UserError(`You don't have enough privileges to set this credit amount`);
principalType: 'USER', }
principalId: userId
};
let count = await models.RoleMapping.count(where);
if (count <= 0) await models.ClientCredit.create({
throw new UserError('The role cannot set this credit amount'); amount: changes.credit,
} clientFk: finalState.id,
workerFk: userId
}, ctx.options);
};
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
app.on('started', function() { app.on('started', function() {

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('Client Model', () => { describe('Client Model', () => {
@ -14,8 +14,8 @@ describe('Client Model', () => {
} }
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
const chatModel = app.models.Chat; const chatModel = models.Chat;
const client = {id: 1101, name: 'Bruce Banner'}; const instance = {id: 1101, name: 'Bruce Banner'};
const previousWorkerId = 1106; // DavidCharlesHaller const previousWorkerId = 1106; // DavidCharlesHaller
const currentWorkerId = 1107; // HankPym const currentWorkerId = 1107; // HankPym
@ -29,7 +29,7 @@ describe('Client Model', () => {
it('should call to the Chat send() method for both workers', async() => { it('should call to the Chat send() method for both workers', async() => {
spyOn(chatModel, 'send').and.callThrough(); spyOn(chatModel, 'send').and.callThrough();
await app.models.Client.notifyAssignment(client, previousWorkerId, currentWorkerId); await models.Client.notifyAssignment(instance, previousWorkerId, currentWorkerId);
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`); expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`); expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
@ -38,7 +38,7 @@ describe('Client Model', () => {
it('should call to the Chat send() method for the previous worker', async() => { it('should call to the Chat send() method for the previous worker', async() => {
spyOn(chatModel, 'send').and.callThrough(); spyOn(chatModel, 'send').and.callThrough();
await app.models.Client.notifyAssignment(client, null, currentWorkerId); await models.Client.notifyAssignment(instance, null, currentWorkerId);
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`); expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
}); });
@ -46,9 +46,69 @@ describe('Client Model', () => {
it('should call to the Chat send() method for the current worker', async() => { it('should call to the Chat send() method for the current worker', async() => {
spyOn(chatModel, 'send').and.callThrough(); spyOn(chatModel, 'send').and.callThrough();
await app.models.Client.notifyAssignment(client, previousWorkerId, null); await models.Client.notifyAssignment(instance, previousWorkerId, null);
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`); expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
}); });
}); });
describe('changeCredit()', () => {
it('should fail to change the credit as a salesAssistant set to zero by a manager', async() => {
const tx = await models.Client.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const context = {options};
// Set credit to zero by a manager
const financialBoss = await models.Account.findOne({
where: {name: 'financialBoss'}
}, options);
context.options.accessToken = {userId: financialBoss.id};
await models.Client.changeCredit(context, instance, {credit: 0});
const salesAssistant = await models.Account.findOne({
where: {name: 'salesAssistant'}
}, options);
context.options.accessToken = {userId: salesAssistant.id};
await models.Client.changeCredit(context, instance, {credit: 300});
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error.message).toEqual(`You can't change the credit set to zero from a manager`);
});
it('should fail to change to a high credit amount as a salesAssistant', async() => {
const tx = await models.Client.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const context = {options};
const salesAssistant = await models.Account.findOne({
where: {name: 'salesAssistant'}
}, options);
context.options.accessToken = {userId: salesAssistant.id};
await models.Client.changeCredit(context, instance, {credit: 99999});
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error.message).toEqual(`You don't have enough privileges to set this credit amount`);
});
});
}); });