feat: separate globalInvoicing in 2 parts
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Vicent Llopis 2022-10-10 12:40:10 +02:00
parent 3da948f3f9
commit 8130cf9e6a
6 changed files with 255 additions and 191 deletions

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('InvoiceOut', 'clientToInvoice', 'WRITE', 'ALLOW', 'ROLE', 'invoicing');

View File

@ -0,0 +1,173 @@
module.exports = Self => {
Self.remoteMethodCtx('clientToInvoice', {
description: 'Get the clients to make global invoicing',
accessType: 'WRITE',
accepts: [
{
arg: 'invoiceDate',
type: 'date',
description: 'The invoice date'
},
{
arg: 'maxShipped',
type: 'date',
description: 'The maximum shipped date'
},
{
arg: 'fromClientId',
type: 'number',
description: 'The minimum client id'
},
{
arg: 'toClientId',
type: 'number',
description: 'The maximum client id',
required: false
},
{
arg: 'companyFk',
type: 'number',
description: 'The company id to invoice'
}
],
returns: {
type: 'object',
root: true
},
http: {
path: '/clientToInvoice',
verb: 'POST'
}
});
Self.clientToInvoice = async(ctx, options) => {
const args = ctx.args;
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const clientToInvoideIds = [];
let query;
try {
query = `
SELECT MAX(issued) issued
FROM vn.invoiceOut io
JOIN vn.time t ON t.dated = io.issued
WHERE io.serial = 'A'
AND t.year = YEAR(?)
AND io.companyFk = ?`;
const [maxIssued] = await Self.rawSql(query, [
args.invoiceDate,
args.companyFk
], myOptions);
const maxSerialDate = maxIssued.issued || args.invoiceDate;
if (args.invoiceDate < maxSerialDate)
args.invoiceDate = maxSerialDate;
if (args.invoiceDate < args.maxShipped)
args.maxShipped = args.invoiceDate;
const minShipped = new Date();
minShipped.setFullYear(minShipped.getFullYear() - 1);
minShipped.setMonth(1);
minShipped.setDate(1);
minShipped.setHours(0, 0, 0, 0);
// Packaging liquidation
const vIsAllInvoiceable = false;
const clientsWithPackaging = await getClientsWithPackaging(ctx, myOptions);
for (let client of clientsWithPackaging) {
await Self.rawSql('CALL packageInvoicing(?, ?, ?, ?, @newTicket)', [
client.id,
args.invoiceDate,
args.companyFk,
vIsAllInvoiceable
], myOptions);
}
const invoiceableClients = await getInvoiceableClients(ctx, myOptions);
if (!invoiceableClients.length) return;
const clientToInvoiceIds = invoiceableClients.map(invoiceableClient => invoiceableClient.id);
const dataArr = new Set(clientToInvoiceIds);
const clientNotRepeatedIds = [...dataArr];
if (tx) await tx.commit();
console.log(invoiceableClients, clientToInvoiceIds, clientNotRepeatedIds);
return clientNotRepeatedIds;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
async function getClientsWithPackaging(ctx, options) {
const models = Self.app.models;
const args = ctx.args;
const query = `SELECT DISTINCT clientFk AS id
FROM ticket t
JOIN ticketPackaging tp ON t.id = tp.ticketFk
JOIN client c ON c.id = t.clientFk
WHERE t.shipped BETWEEN '2017-11-21' AND ?
AND t.clientFk >= ?
AND (t.clientFk <= ? OR ? IS NULL)
AND c.isActive`;
return models.InvoiceOut.rawSql(query, [
args.maxShipped,
args.fromClientId,
args.toClientId,
args.toClientId
], options);
}
async function getInvoiceableClients(ctx, options) {
const models = Self.app.models;
const args = ctx.args;
const minShipped = new Date();
minShipped.setFullYear(minShipped.getFullYear() - 1);
const query = `SELECT
c.id,
SUM(IFNULL
(
s.quantity *
s.price * (100-s.discount)/100,
0)
+ IFNULL(ts.quantity * ts.price,0)
) AS sumAmount,
c.hasToInvoiceByAddress,
c.email,
c.isToBeMailed,
a.id addressFk
FROM ticket t
LEFT JOIN sale s ON s.ticketFk = t.id
LEFT JOIN ticketService ts ON ts.ticketFk = t.id
JOIN address a ON a.id = t.addressFk
JOIN client c ON c.id = t.clientFk
WHERE ISNULL(t.refFk) AND c.id >= ?
AND (t.clientFk <= ? OR ? IS NULL)
AND t.shipped BETWEEN ? AND util.dayEnd(?)
AND t.companyFk = ? AND c.hasToInvoice
AND c.isTaxDataChecked AND c.isActive
GROUP BY c.id, IF(c.hasToInvoiceByAddress,a.id,TRUE) HAVING sumAmount > 0`;
return models.InvoiceOut.rawSql(query, [
args.fromClientId,
args.toClientId,
args.toClientId,
minShipped,
args.maxShipped,
args.companyFk
], options);
}
};

View File

@ -1,35 +1,13 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('globalInvoicing', { Self.remoteMethodCtx('globalInvoicing', {
description: 'Make a global invoice', description: 'Make a global invoice of a client',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [ accepts: [{
{ arg: 'clientId',
arg: 'invoiceDate', type: 'number',
type: 'date', description: 'The client id to invoice',
description: 'The invoice date' required: true
}, }],
{
arg: 'maxShipped',
type: 'date',
description: 'The maximum shipped date'
},
{
arg: 'fromClientId',
type: 'number',
description: 'The minimum client id'
},
{
arg: 'toClientId',
type: 'number',
description: 'The maximum client id',
required: false
},
{
arg: 'companyFk',
type: 'number',
description: 'The company id to invoice'
}
],
returns: { returns: {
type: 'object', type: 'object',
root: true root: true
@ -40,10 +18,10 @@ module.exports = Self => {
} }
}); });
Self.globalInvoicing = async(ctx, options) => { Self.globalInvoicing = async(ctx, clientId, options) => {
const args = ctx.args; const models = Self.app.models;
let tx;
const myOptions = {}; const myOptions = {};
let tx;
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
@ -55,108 +33,65 @@ module.exports = Self => {
const invoicesIds = []; const invoicesIds = [];
const failedClients = []; const failedClients = [];
let query;
try { try {
query = ` const client = await models.Client.findById(clientId, {
SELECT MAX(issued) issued fields: ['id', 'hasToInvoiceByAddress', 'addressFk']
FROM vn.invoiceOut io }, myOptions);
JOIN vn.time t ON t.dated = io.issued try {
WHERE io.serial = 'A' if (client.hasToInvoiceByAddress) {
AND t.year = YEAR(?) await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
AND io.companyFk = ?`; minShipped,
const [maxIssued] = await Self.rawSql(query, [ args.maxShipped,
args.invoiceDate, client.addressFk,
args.companyFk args.companyFk
], myOptions); ], myOptions);
} else {
const maxSerialDate = maxIssued.issued || args.invoiceDate; await Self.rawSql('CALL invoiceFromClient(?, ?, ?)', [
if (args.invoiceDate < maxSerialDate) args.maxShipped,
args.invoiceDate = maxSerialDate;
if (args.invoiceDate < args.maxShipped)
args.maxShipped = args.invoiceDate;
const minShipped = new Date();
minShipped.setFullYear(minShipped.getFullYear() - 1);
minShipped.setMonth(1);
minShipped.setDate(1);
minShipped.setHours(0, 0, 0, 0);
// Packaging liquidation
const vIsAllInvoiceable = false;
const clientsWithPackaging = await getClientsWithPackaging(ctx, myOptions);
for (let client of clientsWithPackaging) {
await Self.rawSql('CALL packageInvoicing(?, ?, ?, ?, @newTicket)', [
client.id,
args.invoiceDate,
args.companyFk,
vIsAllInvoiceable
], myOptions);
}
const invoiceableClients = await getInvoiceableClients(ctx, myOptions);
if (!invoiceableClients.length) return;
for (let client of invoiceableClients) {
try {
if (client.hasToInvoiceByAddress) {
await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [
minShipped,
args.maxShipped,
client.addressFk,
args.companyFk
], myOptions);
} else {
await Self.rawSql('CALL invoiceFromClient(?, ?, ?)', [
args.maxShipped,
client.id,
args.companyFk
], myOptions);
}
// Make invoice
const isSpanishCompany = await getIsSpanishCompany(args.companyFk, myOptions);
// Validates ticket nagative base
const hasAnyNegativeBase = await getNegativeBase(myOptions);
if (hasAnyNegativeBase && isSpanishCompany)
continue;
query = `SELECT invoiceSerial(?, ?, ?) AS serial`;
const [invoiceSerial] = await Self.rawSql(query, [
client.id, client.id,
args.companyFk, args.companyFk
'G'
], myOptions); ], myOptions);
const serialLetter = invoiceSerial.serial;
query = `CALL invoiceOut_new(?, ?, NULL, @invoiceId)`;
await Self.rawSql(query, [
serialLetter,
args.invoiceDate
], myOptions);
const [newInvoice] = await Self.rawSql(`SELECT @invoiceId id`, null, myOptions);
if (newInvoice.id) {
await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions);
query = `INSERT IGNORE INTO invoiceOutQueue(invoiceFk) VALUES(?)`;
await Self.rawSql(query, [newInvoice.id], myOptions);
invoicesIds.push(newInvoice.id);
}
} catch (e) {
failedClients.push({
id: client.id,
stacktrace: e
});
continue;
} }
}
if (failedClients.length > 0) // Make invoice
await notifyFailures(ctx, failedClients, myOptions); const isSpanishCompany = await getIsSpanishCompany(args.companyFk, myOptions);
// Validates ticket nagative base
const hasAnyNegativeBase = await getNegativeBase(myOptions);
if (hasAnyNegativeBase && isSpanishCompany)
return notifyFailures(ctx, failedClients, myOptions); // continue
query = `SELECT invoiceSerial(?, ?, ?) AS serial`;
const [invoiceSerial] = await Self.rawSql(query, [
client.id,
args.companyFk,
'G'
], myOptions);
const serialLetter = invoiceSerial.serial;
query = `CALL invoiceOut_new(?, ?, NULL, @invoiceId)`;
await Self.rawSql(query, [
serialLetter,
args.invoiceDate
], myOptions);
const [newInvoice] = await Self.rawSql(`SELECT @invoiceId id`, null, myOptions);
if (newInvoice.id) {
await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions);
query = `INSERT IGNORE INTO invoiceOutQueue(invoiceFk) VALUES(?)`;
await Self.rawSql(query, [newInvoice.id], myOptions);
invoicesIds.push(newInvoice.id);
}
} catch (e) {
failedClients.push({
id: client.id,
stacktrace: e
});
return notifyFailures(ctx, failedClients, myOptions); // continue
}
// }
if (tx) await tx.commit(); if (tx) await tx.commit();
} catch (e) { } catch (e) {
@ -189,66 +124,6 @@ module.exports = Self => {
return supplierCompany && supplierCompany.total; return supplierCompany && supplierCompany.total;
} }
async function getClientsWithPackaging(ctx, options) {
const models = Self.app.models;
const args = ctx.args;
const query = `SELECT DISTINCT clientFk AS id
FROM ticket t
JOIN ticketPackaging tp ON t.id = tp.ticketFk
JOIN client c ON c.id = t.clientFk
WHERE t.shipped BETWEEN '2017-11-21' AND ?
AND t.clientFk >= ?
AND (t.clientFk <= ? OR ? IS NULL)
AND c.isActive`;
return models.InvoiceOut.rawSql(query, [
args.maxShipped,
args.fromClientId,
args.toClientId,
args.toClientId
], options);
}
async function getInvoiceableClients(ctx, options) {
const models = Self.app.models;
const args = ctx.args;
const minShipped = new Date();
minShipped.setFullYear(minShipped.getFullYear() - 1);
const query = `SELECT
c.id,
SUM(IFNULL
(
s.quantity *
s.price * (100-s.discount)/100,
0)
+ IFNULL(ts.quantity * ts.price,0)
) AS sumAmount,
c.hasToInvoiceByAddress,
c.email,
c.isToBeMailed,
a.id addressFk
FROM ticket t
LEFT JOIN sale s ON s.ticketFk = t.id
LEFT JOIN ticketService ts ON ts.ticketFk = t.id
JOIN address a ON a.id = t.addressFk
JOIN client c ON c.id = t.clientFk
WHERE ISNULL(t.refFk) AND c.id >= ?
AND (t.clientFk <= ? OR ? IS NULL)
AND t.shipped BETWEEN ? AND util.dayEnd(?)
AND t.companyFk = ? AND c.hasToInvoice
AND c.isTaxDataChecked AND c.isActive
GROUP BY c.id, IF(c.hasToInvoiceByAddress,a.id,TRUE) HAVING sumAmount > 0`;
return models.InvoiceOut.rawSql(query, [
args.fromClientId,
args.toClientId,
args.toClientId,
minShipped,
args.maxShipped,
args.companyFk
], options);
}
async function notifyFailures(ctx, failedClients, options) { async function notifyFailures(ctx, failedClients, options) {
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;

View File

@ -7,6 +7,7 @@ module.exports = Self => {
require('../methods/invoiceOut/book')(Self); require('../methods/invoiceOut/book')(Self);
require('../methods/invoiceOut/createPdf')(Self); require('../methods/invoiceOut/createPdf')(Self);
require('../methods/invoiceOut/createManualInvoice')(Self); require('../methods/invoiceOut/createManualInvoice')(Self);
require('../methods/invoiceOut/clientToInvoice')(Self);
require('../methods/invoiceOut/globalInvoicing')(Self); require('../methods/invoiceOut/globalInvoicing')(Self);
require('../methods/invoiceOut/refund')(Self); require('../methods/invoiceOut/refund')(Self);
require('../methods/invoiceOut/invoiceEmail')(Self); require('../methods/invoiceOut/invoiceEmail')(Self);

View File

@ -20,6 +20,8 @@
<vn-horizontal> <vn-horizontal>
<vn-icon vn-none icon="warning"></vn-icon> <vn-icon vn-none icon="warning"></vn-icon>
<span vn-none translate>Adding invoices to queue...</span> <span vn-none translate>Adding invoices to queue...</span>
{{$ctrl.lastClientId}}
<span translate>{{::$ctrl.lastClientId || 'Vacío'}}</span>
</vn-horizontal> </vn-horizontal>
</div> </div>
<vn-horizontal> <vn-horizontal>

View File

@ -58,8 +58,18 @@ class Controller extends Dialog {
throw new Error('Choose a valid clients range'); throw new Error('Choose a valid clients range');
this.isInvoicing = true; this.isInvoicing = true;
return this.$http.post(`InvoiceOuts/globalInvoicing`, this.invoice) return this.$http.post(`InvoiceOuts/clientToInvoice`, this.invoice)
.then(() => super.responseHandler(response)) .then(res => {
this.lastClientId = res.data[res.data.length - 1];
console.log(this.lastClientId);
})
// .then(() => {
// for (let clientId of res.data) {
// const params = {clientId: clientId};
// this.$http.post(`InvoiceOuts/globalInvoicing`, params);
// }
// })
// .then(() => super.responseHandler(response))
.then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) .then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
.finally(() => this.isInvoicing = false); .finally(() => this.isInvoicing = false);
} catch (e) { } catch (e) {