salix/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js

209 lines
7.5 KiB
JavaScript

const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('createManualInvoice', {
description: 'Make a manual invoice',
accessType: 'WRITE',
accepts: [
{
arg: 'clientFk',
type: 'any',
description: 'The invoiceable client id'
},
{
arg: 'addressFk',
type: 'any',
description: 'The address id'
},
{
arg: 'ticketFk',
type: 'any',
description: 'The invoiceable ticket id'
},
{
arg: 'maxShipped',
type: 'date',
description: 'The maximum shipped date'
},
{
arg: 'serial',
type: 'string',
description: 'The invoice serial',
required: true
},
{
arg: 'taxArea',
type: 'string',
description: 'The invoice tax area'
},
{
arg: 'reference',
type: 'string',
description: 'The invoice reference'
}
],
returns: {
type: 'object',
root: true
},
http: {
path: '/createManualInvoice',
verb: 'POST'
}
});
Self.createManualInvoice =
async(ctx, clientFk, addressFk, ticketFk, maxShipped, serial, taxArea, reference, options) => {
if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`);
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
let companyFk;
let newInvoice;
let query;
try {
if (ticketFk) {
const ticket = await models.Ticket.findById(ticketFk, {
fields: ['clientFk', 'companyFk', 'shipped', 'refFk', 'totalWithVat']
}, myOptions);
const company = await models.Company.findById(ticket.companyFk, {
fields: ['code']
}, myOptions);
clientFk = ticket.clientFk;
maxShipped = ticket.shipped;
companyFk = ticket.companyFk;
if (ticket.refFk)
throw new UserError('This ticket is already invoiced');
if (ticket.totalWithVat == 0)
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyFk, myOptions);
if (hasNegativeBase && company.code == 'VNL')
throw new UserError(`A ticket with a negative base can't be invoiced`);
} else {
if (!maxShipped)
throw new UserError(`Max shipped required`);
if (addressFk) {
const address = await models.Address.findById(addressFk, {
fields: ['clientFk']
}, myOptions);
if (clientFk && clientFk !== address.clientFk)
throw new UserError('The provided clientFk does not match');
}
const company = await models.Ticket.findOne({
fields: ['companyFk'],
where: {
clientFk: clientFk,
shipped: {lte: maxShipped}
}
}, myOptions);
companyFk = company.companyFk;
}
const isClientInvoiceable = await isInvoiceable(clientFk, myOptions);
if (!isClientInvoiceable)
throw new UserError(`This client is not invoiceable`);
const tomorrow = Date.vnNew();
tomorrow.setDate(tomorrow.getDate() + 1);
if (maxShipped >= tomorrow)
throw new UserError(`Can't invoice to future`);
const maxInvoiceDate = await getMaxIssued(serial, companyFk, myOptions);
if (Date.vnNew() < maxInvoiceDate)
throw new UserError(`Can't invoice to past`);
if (ticketFk) {
query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [
ticketFk,
serial,
taxArea,
reference
], myOptions);
} else if (addressFk) {
query = `CALL invoiceOut_newFromAddress(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [
addressFk,
serial,
maxShipped,
companyFk,
taxArea,
reference
], myOptions);
} else {
query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
await Self.rawSql(query, [
clientFk,
serial,
maxShipped,
companyFk,
taxArea,
reference
], myOptions);
}
[newInvoice] = await Self.rawSql(`SELECT @newInvoiceId id`, null, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
if (!newInvoice.id) throw new UserError('It was not able to create the invoice');
await Self.createPdf(ctx, newInvoice.id);
return newInvoice;
};
async function isInvoiceable(clientFk, options) {
const models = Self.app.models;
const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable
FROM client
WHERE id = ?`;
const [result] = await models.InvoiceOut.rawSql(query, [clientFk], options);
return result.invoiceable;
}
async function getNegativeBase(maxShipped, clientFk, companyFk, options) {
const models = Self.app.models;
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
[maxShipped, clientFk, companyFk], options
);
const query = 'SELECT vn.hasAnyNegativeBase() AS base';
const [result] = await models.InvoiceOut.rawSql(query, [], options);
return result.base;
}
async function getMaxIssued(serial, companyFk, options) {
const models = Self.app.models;
const query = `SELECT MAX(issued) AS issued
FROM invoiceOut
WHERE serial = ? AND companyFk = ?`;
const [maxIssued] = await models.InvoiceOut.rawSql(query,
[serial, companyFk], options);
const maxInvoiceDate = maxIssued?.issued || Date.vnNew();
return maxInvoiceDate;
}
};