refs #6915 test_master24_8 #2067

Merged
alexm merged 561 commits from test_master24_8 into master 2024-02-22 07:31:34 +00:00
20 changed files with 192 additions and 207 deletions
Showing only changes of commit 7283945cf2 - Show all commits

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
VALUES ('Ticket','canEditWeekly','WRITE','ALLOW','ROLE','buyer');

View File

@ -178,5 +178,9 @@
"The renew period has not been exceeded": "The renew period has not been exceeded", "The renew period has not been exceeded": "The renew period has not been exceeded",
"You can not use the same password": "You can not use the same password", "You can not use the same password": "You can not use the same password",
"Valid priorities": "Valid priorities: %d", "Valid priorities": "Valid priorities: %d",
"Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}" "Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}",
"You don't have enough privileges.": "You don't have enough privileges.",
"This ticket is locked.": "This ticket is locked.",
"This ticket is not editable.": "This ticket is not editable.",
"The ticket doesn't exist.": "The ticket doesn't exist."
} }

View File

@ -307,5 +307,9 @@
"Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}", "Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias", "The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado", "You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado" "This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked.": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket."
} }

View File

@ -1,4 +1,3 @@
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
@ -47,9 +46,7 @@ module.exports = Self => {
} }
try { try {
const isEditable = await models.Ticket.isEditable(ctx, id, myOptions); await models.Ticket.isEditableOrThrow(ctx, id, myOptions);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
const item = await models.Item.findById(itemId, null, myOptions); const item = await models.Item.findById(itemId, null, myOptions);
const ticket = await models.Ticket.findById(id, { const ticket = await models.Ticket.findById(id, {

View File

@ -1,4 +1,3 @@
const UserError = require('vn-loopback/util/user-error');
const loggable = require('vn-loopback/util/log'); const loggable = require('vn-loopback/util/log');
module.exports = Self => { module.exports = Self => {
@ -116,10 +115,7 @@ module.exports = Self => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const isEditable = await models.Ticket.isEditable(ctx, args.id, myOptions); await models.Ticket.isEditableOrThrow(ctx, args.id, myOptions);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
const editZone = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editZone', 'WRITE'); const editZone = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editZone', 'WRITE');
if (!editZone) { if (!editZone) {

View File

@ -20,41 +20,12 @@ module.exports = Self => {
}); });
Self.isEditable = async(ctx, id, options) => { Self.isEditable = async(ctx, id, options) => {
const models = Self.app.models; try {
const myOptions = {}; await Self.isEditableOrThrow(ctx, id, options);
} catch (e) {
if (typeof options == 'object') if (e.name === 'ForbiddenError') return false;
Object.assign(myOptions, options); throw e;
const state = await models.TicketState.findOne({
where: {ticketFk: id}
}, myOptions);
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
const alertLevel = state ? state.alertLevel : null;
const ticket = await models.Ticket.findById(id, {
fields: ['clientFk'],
include: [{
relation: 'client',
scope: {
include: {
relation: 'type'
} }
}
}]
}, myOptions);
const isLocked = await models.Ticket.isLocked(id, myOptions);
const isWeekly = await models.TicketWeekly.findOne({where: {ticketFk: id}}, myOptions);
const alertLevelGreaterThanZero = (alertLevel && alertLevel > 0);
const isNormalClient = ticket && ticket.client().type().code == 'normal';
const isEditable = !(alertLevelGreaterThanZero && isNormalClient);
if (ticket && (isEditable || isRoleAdvanced) && !isLocked && !isWeekly)
return true; return true;
return false;
}; };
}; };

View File

@ -0,0 +1,49 @@
const ForbiddenError = require('vn-loopback/util/forbiddenError');
module.exports = Self => {
Self.isEditableOrThrow = async(ctx, id, options) => {
const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const state = await models.TicketState.findOne({
where: {ticketFk: id}
}, myOptions);
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
const canEditWeeklyTicket = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'canEditWeekly', 'WRITE');
const alertLevel = state ? state.alertLevel : null;
const ticket = await models.Ticket.findById(id, {
fields: ['clientFk'],
include: [{
relation: 'client',
scope: {
include: {
relation: 'type'
}
}
}]
}, myOptions);
const isLocked = await models.Ticket.isLocked(id, myOptions);
const isWeekly = await models.TicketWeekly.findOne({where: {ticketFk: id}}, myOptions);
const alertLevelGreaterThanZero = (alertLevel && alertLevel > 0);
const isNormalClient = ticket && ticket.client().type().code == 'normal';
const isEditable = !(alertLevelGreaterThanZero && isNormalClient);
if (!ticket)
throw new ForbiddenError(`The ticket doesn't exist.`);
if (!isEditable && !isRoleAdvanced)
throw new ForbiddenError(`This ticket is not editable.`);
if (isLocked)
throw new ForbiddenError(`This ticket is locked.`);
if (isWeekly && !canEditWeeklyTicket)
throw new ForbiddenError(`You don't have enough privileges.`);
};
};

View File

@ -1,5 +1,3 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('priceDifference', { Self.remoteMethodCtx('priceDifference', {
description: 'Returns sales with price difference if the ticket is editable', description: 'Returns sales with price difference if the ticket is editable',
@ -72,10 +70,7 @@ module.exports = Self => {
} }
try { try {
const isEditable = await Self.isEditable(ctx, args.id, myOptions); await Self.isEditableOrThrow(ctx, args.id, myOptions);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
const editZone = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editZone', 'WRITE'); const editZone = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editZone', 'WRITE');
if (!editZone) { if (!editZone) {

View File

@ -1,4 +1,3 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('recalculateComponents', { Self.remoteMethodCtx('recalculateComponents', {
description: 'Calculates the price of a sale and its components', description: 'Calculates the price of a sale and its components',
@ -33,10 +32,7 @@ module.exports = Self => {
} }
try { try {
const isEditable = await Self.isEditable(ctx, id, myOptions); await Self.isEditableOrThrow(ctx, id, myOptions);
if (!isEditable)
throw new UserError(`The current ticket can't be modified`);
const recalculation = await Self.rawSql('CALL vn.ticket_recalcComponents(?, NULL)', [id], myOptions); const recalculation = await Self.rawSql('CALL vn.ticket_recalcComponents(?, NULL)', [id], myOptions);

View File

@ -39,10 +39,7 @@ module.exports = Self => {
const ticketToDelete = await models.Ticket.findById(id, {fields: ['isDeleted']}, myOptions); const ticketToDelete = await models.Ticket.findById(id, {fields: ['isDeleted']}, myOptions);
if (ticketToDelete.isDeleted) return false; if (ticketToDelete.isDeleted) return false;
const isEditable = await Self.isEditable(ctx, id, myOptions); await Self.isEditableOrThrow(ctx, id, myOptions);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
// Check if ticket has refunds // Check if ticket has refunds
const ticketRefunds = await models.TicketRefund.find({ const ticketRefunds = await models.TicketRefund.find({

View File

@ -97,6 +97,6 @@ describe('ticket addSale()', () => {
error = e; error = e;
} }
expect(error.message).toEqual(`The sales of this ticket can't be modified`); expect(error.message).toEqual(`This ticket is locked.`);
}); });
}); });

View File

@ -1,156 +1,37 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('ticket isEditable()', () => { describe('isEditable()', () => {
it('should return false if the given ticket does not exist', async() => { it('should return false if It is able to edit', async() => {
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
let result; let result;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const ctx = { const ctx = {req: {accessToken: {userId: 35}}};
req: {accessToken: {userId: 9}} result = await models.Ticket.isEditable(ctx, 5, options);
};
result = await models.Ticket.isEditable(ctx, 9999, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (error) {
await tx.rollback(); await tx.rollback();
throw e; throw e;
} }
expect(result).toEqual(false); expect(result).toBeFalse();
}); });
it(`should return false if the given ticket isn't invoiced but isDeleted`, async() => { it('should return true if It is able to edit', async() => {
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
let result; let result;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const deletedTicket = await models.Ticket.findOne({ const ctx = {req: {accessToken: {userId: 35}}};
where: { result = await models.Ticket.isEditable(ctx, 15, options);
invoiceOut: null,
isDeleted: true
},
fields: ['id']
});
const ctx = {
req: {accessToken: {userId: 9}}
};
result = await models.Ticket.isEditable(ctx, deletedTicket.id, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (error) {
await tx.rollback(); await tx.rollback();
throw e; throw e;
} }
expect(result).toEqual(false); expect(result).toBeTrue();
});
it('should return true if the given ticket is editable', async() => {
const tx = await models.Ticket.beginTransaction({});
let result;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 9}}
};
result = await models.Ticket.isEditable(ctx, 16, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(result).toEqual(true);
});
it('should not be able to edit a deleted or invoiced ticket even for salesAssistant', async() => {
const tx = await models.Ticket.beginTransaction({});
let result;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 21}}
};
result = await models.Ticket.isEditable(ctx, 19, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(result).toEqual(false);
});
it('should not be able to edit a deleted or invoiced ticket even for productionBoss', async() => {
const tx = await models.Ticket.beginTransaction({});
let result;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 50}}
};
result = await models.Ticket.isEditable(ctx, 19, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(result).toEqual(false);
});
it('should not be able to edit a deleted or invoiced ticket even for salesPerson', async() => {
const tx = await models.Ticket.beginTransaction({});
let result;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 18}}
};
result = await models.Ticket.isEditable(ctx, 19, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(result).toEqual(false);
});
it('should not be able to edit if is a ticket weekly', async() => {
const tx = await models.Ticket.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}};
const result = await models.Ticket.isEditable(ctx, 15, options);
expect(result).toEqual(false);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -0,0 +1,96 @@
const models = require('vn-loopback/server/server').models;
describe('ticket isEditableOrThrow()', () => {
it('should throw an error as the ticket does not exist', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 9}}
};
await models.Ticket.isEditableOrThrow(ctx, 9999, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toEqual(`The ticket doesn't exist.`);
});
it('should throw an error as this ticket is not editable', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 1}}
};
await models.Ticket.isEditableOrThrow(ctx, 8, options);
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error.message).toEqual(`This ticket is not editable.`);
});
it('should throw an error as this ticket is locked.', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {
req: {accessToken: {userId: 18}}
};
await models.Ticket.isEditableOrThrow(ctx, 19, options);
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error.message).toEqual(`This ticket is locked.`);
});
it('should throw an error as you do not have enough privileges.', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}};
await models.Ticket.isEditableOrThrow(ctx, 15, options);
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
expect(error.message).toEqual(`You don't have enough privileges.`);
});
it('should return undefined if It can be edited', async() => {
const tx = await models.Ticket.beginTransaction({});
let result;
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 35}}};
result = await models.Ticket.isEditableOrThrow(ctx, 15, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(result).toBeUndefined();
});
});

View File

@ -1,5 +1,6 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
const ForbiddenError = require('vn-loopback/util/forbiddenError');
describe('sale priceDifference()', () => { describe('sale priceDifference()', () => {
it('should return ticket price differences', async() => { it('should return ticket price differences', async() => {
@ -59,7 +60,7 @@ describe('sale priceDifference()', () => {
await tx.rollback(); await tx.rollback();
} }
expect(error).toEqual(new UserError(`The sales of this ticket can't be modified`)); expect(error).toEqual(new ForbiddenError(`This ticket is not editable.`));
}); });
it('should return ticket movable', async() => { it('should return ticket movable', async() => {

View File

@ -1,4 +1,5 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const ForbiddenError = require('vn-loopback/util/forbiddenError');
describe('ticket recalculateComponents()', () => { describe('ticket recalculateComponents()', () => {
const ticketId = 11; const ticketId = 11;
@ -38,6 +39,6 @@ describe('ticket recalculateComponents()', () => {
error = e; error = e;
} }
expect(error).toEqual(new Error(`The current ticket can't be modified`)); expect(error).toEqual(new ForbiddenError(`This ticket is locked.`));
}); });
}); });

View File

@ -23,7 +23,7 @@ describe('Ticket transferClient()', () => {
error = e; error = e;
} }
expect(error.message).toEqual(`The current ticket can't be modified`); expect(error.message).toEqual(`This ticket is locked.`);
}); });
it('should be assigned a different clientFk', async() => { it('should be assigned a different clientFk', async() => {

View File

@ -33,7 +33,7 @@ describe('sale transferSales()', () => {
error = e; error = e;
} }
expect(error.message).toEqual(`The sales of this ticket can't be modified`); expect(error.message).toEqual(`This ticket is not editable.`);
}); });
it('should throw an error if the receiving ticket is not editable', async() => { it('should throw an error if the receiving ticket is not editable', async() => {

View File

@ -1,4 +1,3 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('transferClient', { Self.remoteMethodCtx('transferClient', {
description: 'Transfering ticket to another client', description: 'Transfering ticket to another client',
@ -29,10 +28,7 @@ module.exports = Self => {
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const isEditable = await Self.isEditable(ctx, id, myOptions); await Self.isEditableOrThrow(ctx, id, myOptions);
if (!isEditable)
throw new UserError(`The current ticket can't be modified`);
const ticket = await models.Ticket.findById( const ticket = await models.Ticket.findById(
id, id,

View File

@ -1,4 +1,4 @@
let UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('transferSales', { Self.remoteMethodCtx('transferSales', {
@ -48,9 +48,7 @@ module.exports = Self => {
} }
try { try {
const isEditable = await models.Ticket.isEditable(ctx, id, myOptions); await models.Ticket.isEditableOrThrow(ctx, id, myOptions);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
if (ticketId) { if (ticketId) {
const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions); const isReceiverEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);

View File

@ -6,6 +6,7 @@ module.exports = function(Self) {
require('../methods/ticket/componentUpdate')(Self); require('../methods/ticket/componentUpdate')(Self);
require('../methods/ticket/new')(Self); require('../methods/ticket/new')(Self);
require('../methods/ticket/isEditable')(Self); require('../methods/ticket/isEditable')(Self);
require('../methods/ticket/isEditableOrThrow')(Self);
require('../methods/ticket/setDeleted')(Self); require('../methods/ticket/setDeleted')(Self);
require('../methods/ticket/restore')(Self); require('../methods/ticket/restore')(Self);
require('../methods/ticket/getSales')(Self); require('../methods/ticket/getSales')(Self);