refactor(client): added restriction to change the credit amount from zero set by a manager #731
|
@ -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"
|
||||||
}
|
}
|
|
@ -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,43 +322,55 @@ 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;
|
||||||
|
const lastWorkerId = lastCredit && lastCredit.workerFk;
|
||||||
|
const lastWorkerIsManager = await models.Account.hasRole(lastWorkerId, 'manager', ctx.options);
|
||||||
|
|
||||||
|
if (lastAmount == 0 && lastWorkerIsManager)
|
||||||
|
throw new UserError(`You can't change the credit set to zero from a manager`);
|
||||||
|
|
||||||
|
const creditLimits = await models.ClientCreditLimit.find({
|
||||||
fields: ['roleFk'],
|
fields: ['roleFk'],
|
||||||
where: {
|
where: {
|
||||||
maxAmount: {gt: ctx.data.credit}
|
maxAmount: {gte: changes.credit}
|
||||||
}
|
}
|
||||||
};
|
}, ctx.options);
|
||||||
|
|
||||||
let limits = await models.ClientCreditLimit.find(filter);
|
const requiredRoles = [];
|
||||||
|
for (limit of creditLimits)
|
||||||
if (limits.length == 0)
|
|
||||||
throw new UserError('Credit limits not found');
|
|
||||||
|
|
||||||
// Si el usuario no tiene alguno de los roles no continua
|
|
||||||
|
|
||||||
let requiredRoles = [];
|
|
||||||
for (limit of limits)
|
|
||||||
requiredRoles.push(limit.roleFk);
|
requiredRoles.push(limit.roleFk);
|
||||||
|
|
||||||
let where = {
|
const userRequiredRoles = await models.RoleMapping.count({
|
||||||
roleId: {inq: requiredRoles},
|
roleId: {inq: requiredRoles},
|
||||||
principalType: 'USER',
|
principalType: 'USER',
|
||||||
principalId: userId
|
principalId: userId
|
||||||
};
|
}, ctx.options);
|
||||||
let count = await models.RoleMapping.count(where);
|
|
||||||
|
|
||||||
if (count <= 0)
|
if (userRequiredRoles <= 0)
|
||||||
throw new UserError('The role cannot set this credit amount');
|
throw new UserError(`You don't have enough privileges to set this credit amount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await models.ClientCredit.create({
|
||||||
|
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() {
|
||||||
let account = app.models.Account;
|
let account = app.models.Account;
|
||||||
|
|
|
@ -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`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue