#7019 fix(createManualInvoice): add throw error #2175

Merged
jorgep merged 7 commits from 7019_fix_createManualInvoice into dev 2024-04-25 15:30:19 +00:00
5 changed files with 57 additions and 76 deletions

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### Fixed
- (Worker -> time-control) Corrección de errores - (Worker -> time-control) Corrección de errores
- (InvoiceOut -> Crear factura) Cuando falla al crear una factura, se devuelve un error
## [24.18.01] - 2024-05-07 ## [24.18.01] - 2024-05-07

View File

@ -40,7 +40,7 @@ describe('InvoiceOut manual invoice path', () => {
await page.waitToClick(selectors.invoiceOutIndex.createInvoice); await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm); await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Max Eisenhardt'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Bruce Wayne');
Review

Se estaba intentando facturar el mismo ticket. El ticket 15 es de este cliente, por tanto falla. Antes devolvía id: null y no estaba controlado para que lance un error.

Se estaba intentando facturar el mismo ticket. El ticket 15 es de este cliente, por tanto falla. Antes devolvía id: null y no estaba controlado para que lance un error.
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional');
await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national');
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice); await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);

View File

@ -353,5 +353,7 @@
"This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario", "This password can only be changed by the user themselves": "Esta contraseña solo puede ser modificada por el propio usuario",
"They're not your subordinate": "No es tu subordinado/a.", "They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados", "No results found": "No se han encontrado resultados",
"InvoiceIn is already booked": "La factura recibida está contabilizada" "InvoiceIn is already booked": "La factura recibida está contabilizada",
} "Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura"
}

View File

@ -46,12 +46,11 @@ module.exports = Self => {
} }
}); });
Self.createManualInvoice = async(ctx, options) => { Self.createManualInvoice = async(ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options) => {
if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`);
const models = Self.app.models; const models = Self.app.models;
const args = ctx.args;
let tx;
const myOptions = {userId: ctx.req.accessToken.userId}; const myOptions = {userId: ctx.req.accessToken.userId};
let tx;
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
@ -61,18 +60,15 @@ module.exports = Self => {
myOptions.transaction = tx; myOptions.transaction = tx;
} }
const ticketId = args.ticketFk;
let clientId = args.clientFk;
let maxShipped = args.maxShipped;
let companyId; let companyId;
let newInvoice; let newInvoice;
let query; let query;
try { try {
if (ticketId) { if (ticketFk) {
const ticket = await models.Ticket.findById(ticketId, null, myOptions); const ticket = await models.Ticket.findById(ticketFk, null, myOptions);
const company = await models.Company.findById(ticket.companyFk, null, myOptions); const company = await models.Company.findById(ticket.companyFk, null, myOptions);
clientId = ticket.clientFk; clientFk = ticket.clientFk;
maxShipped = ticket.shipped; maxShipped = ticket.shipped;
companyId = ticket.companyFk; companyId = ticket.companyFk;
@ -85,7 +81,7 @@ module.exports = Self => {
throw new UserError(`A ticket with an amount of zero can't be invoiced`); throw new UserError(`A ticket with an amount of zero can't be invoiced`);
// Validates ticket nagative base // Validates ticket nagative base
const hasNegativeBase = await getNegativeBase(maxShipped, clientId, companyId, myOptions); const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyId, myOptions);
if (hasNegativeBase && company.code == 'VNL') if (hasNegativeBase && company.code == 'VNL')
throw new UserError(`A ticket with a negative base can't be invoiced`); throw new UserError(`A ticket with a negative base can't be invoiced`);
} else { } else {
@ -95,7 +91,7 @@ module.exports = Self => {
const company = await models.Ticket.findOne({ const company = await models.Ticket.findOne({
fields: ['companyFk'], fields: ['companyFk'],
where: { where: {
clientFk: clientId, clientFk: clientFk,
shipped: {lte: maxShipped} shipped: {lte: maxShipped}
} }
}, myOptions); }, myOptions);
@ -103,7 +99,7 @@ module.exports = Self => {
} }
// Validate invoiceable client // Validate invoiceable client
const isClientInvoiceable = await isInvoiceable(clientId, myOptions); const isClientInvoiceable = await isInvoiceable(clientFk, myOptions);
if (!isClientInvoiceable) if (!isClientInvoiceable)
throw new UserError(`This client is not invoiceable`); throw new UserError(`This client is not invoiceable`);
@ -114,27 +110,27 @@ module.exports = Self => {
if (maxShipped >= tomorrow) if (maxShipped >= tomorrow)
throw new UserError(`Can't invoice to future`); throw new UserError(`Can't invoice to future`);
const maxInvoiceDate = await getMaxIssued(args.serial, companyId, myOptions); const maxInvoiceDate = await getMaxIssued(serial, companyId, myOptions);
if (Date.vnNew() < maxInvoiceDate) if (Date.vnNew() < maxInvoiceDate)
throw new UserError(`Can't invoice to past`); throw new UserError(`Can't invoice to past`);
if (ticketId) { if (ticketFk) {
query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`; query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [ await Self.rawSql(query, [
ticketId, ticketFk,
args.serial, serial,
args.taxArea, taxArea,
args.reference reference
], myOptions); ], myOptions);
} else { } else {
query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`; query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [ await Self.rawSql(query, [
clientId, clientFk,
args.serial, serial,
maxShipped, maxShipped,
companyId, companyId,
args.taxArea, taxArea,
args.reference reference
], myOptions); ], myOptions);
} }
@ -146,26 +142,27 @@ module.exports = Self => {
throw e; throw e;
} }
if (newInvoice.id) if (!newInvoice.id) throw new UserError('It was not able to create the invoice');
await Self.createPdf(ctx, newInvoice.id);
await Self.createPdf(ctx, newInvoice.id);
return newInvoice; return newInvoice;
}; };
async function isInvoiceable(clientId, options) { async function isInvoiceable(clientFk, options) {
const models = Self.app.models; const models = Self.app.models;
const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable
FROM client FROM client
WHERE id = ?`; WHERE id = ?`;
const [result] = await models.InvoiceOut.rawSql(query, [clientId], options); const [result] = await models.InvoiceOut.rawSql(query, [clientFk], options);
return result.invoiceable; return result.invoiceable;
} }
async function getNegativeBase(maxShipped, clientId, companyId, options) { async function getNegativeBase(maxShipped, clientFk, companyId, options) {
const models = Self.app.models; const models = Self.app.models;
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)', await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
[maxShipped, clientId, companyId], options [maxShipped, clientFk, companyId], options
); );
const query = 'SELECT vn.hasAnyNegativeBase() AS base'; const query = 'SELECT vn.hasAnyNegativeBase() AS base';
const [result] = await models.InvoiceOut.rawSql(query, [], options); const [result] = await models.InvoiceOut.rawSql(query, [], options);

View File

@ -1,13 +1,10 @@
const models = require('vn-loopback/server/server').models; const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('InvoiceOut createManualInvoice()', () => { describe('InvoiceOut createManualInvoice()', () => {
const userId = 1;
const ticketId = 16; const ticketId = 16;
const clientId = 1106; const clientId = 1106;
const activeCtx = { const activeCtx = {accessToken: {userId: 1}};
accessToken: {userId: userId},
};
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
it('should throw an error trying to invoice again', async() => { it('should throw an error trying to invoice again', async() => {
@ -18,13 +15,8 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
ctx.args = { await createInvoice(ctx, options, undefined, ticketId);
ticketFk: ticketId, await createInvoice(ctx, options, undefined, ticketId);
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await models.InvoiceOut.createManualInvoice(ctx, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -47,17 +39,9 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
const ticket = await models.Ticket.findById(ticketId, null, options); const ticket = await models.Ticket.findById(ticketId, null, options);
await ticket.updateAttributes({ await ticket.updateAttributes({totalWithVat: 0}, options);
totalWithVat: 0
}, options);
ctx.args = {
ticketFk: ticketId,
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await createInvoice(ctx, options, undefined, ticketId);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
error = e; error = e;
@ -75,13 +59,7 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
ctx.args = { await createInvoice(ctx, options, clientId);
clientFk: clientId,
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
error = e; error = e;
@ -103,16 +81,9 @@ describe('InvoiceOut createManualInvoice()', () => {
let error; let error;
try { try {
const client = await models.Client.findById(clientId, null, options); const client = await models.Client.findById(clientId, null, options);
await client.updateAttributes({ await client.updateAttributes({isTaxDataChecked: false}, options);
isTaxDataChecked: false
}, options);
ctx.args = { await createInvoice(ctx, options, undefined, ticketId);
ticketFk: ticketId,
serial: 'T',
taxArea: 'CEE'
};
await models.InvoiceOut.createManualInvoice(ctx, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -130,12 +101,7 @@ describe('InvoiceOut createManualInvoice()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
try { try {
ctx.args = { const result = await createInvoice(ctx, options, undefined, ticketId);
ticketFk: ticketId,
serial: 'T',
taxArea: 'CEE'
};
const result = await models.InvoiceOut.createManualInvoice(ctx, options);
expect(result.id).toEqual(jasmine.any(Number)); expect(result.id).toEqual(jasmine.any(Number));
@ -146,3 +112,18 @@ describe('InvoiceOut createManualInvoice()', () => {
} }
}); });
}); });
function createInvoice(
Review

Se repetía mucho lo mismo.

Se repetía mucho lo mismo.
ctx,
options,
clientFk = undefined,
ticketFk = undefined,
maxShipped = undefined,
serial = 'T',
taxArea = 'CEE',
reference = undefined
) {
return models.InvoiceOut.createManualInvoice(
ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options
);
}