From c923526342647d34a2ee2aeec27fce0eaa384bfe Mon Sep 17 00:00:00 2001 From: joan Date: Thu, 28 Apr 2022 08:14:33 +0200 Subject: [PATCH 1/5] refactor(invoicing): send invoice to print queue --- .../10451-april/00-invoiceOut_queue.sql | 13 ++++++ .../methods/invoiceOut/globalInvoicing.js | 7 ++- .../item/specs/lastEntriesFilter.spec.js | 2 +- print/methods/notify/invocing.js | 45 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 db/changes/10451-april/00-invoiceOut_queue.sql create mode 100644 print/methods/notify/invocing.js diff --git a/db/changes/10451-april/00-invoiceOut_queue.sql b/db/changes/10451-april/00-invoiceOut_queue.sql new file mode 100644 index 000000000..80f6aed40 --- /dev/null +++ b/db/changes/10451-april/00-invoiceOut_queue.sql @@ -0,0 +1,13 @@ +create table `vn`.`invoiceOut_queue` +( + invoiceFk int(10) unsigned not null, + dated datetime default now() null, + printed tinyint(1) default 0 null, + constraint invoiceOut_queue_pk + primary key (invoiceFk), + constraint invoiceOut_queue_invoiceOut_id_fk + foreign key (invoiceFk) references invoiceOut (id) + on update cascade on delete cascade +) + comment 'Queue for PDF invoicing'; + diff --git a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js index 6b901872e..f2c988909 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js +++ b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js @@ -140,6 +140,9 @@ module.exports = Self => { if (newInvoice.id) { await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions); + query = `INSERT IGNORE INTO invoiceOut_queue(invoiceFk) VALUES(?)`; + await Self.rawSql(query, [newInvoice.id], myOptions); + invoicesIds.push(newInvoice.id); } } catch (e) { @@ -161,8 +164,8 @@ module.exports = Self => { } // Print invoices PDF - for (let invoiceId of invoicesIds) - await Self.createPdf(ctx, invoiceId); + // for (let invoiceId of invoicesIds) + // await Self.createPdf(ctx, invoiceId); return invoicesIds; }; diff --git a/modules/item/back/methods/item/specs/lastEntriesFilter.spec.js b/modules/item/back/methods/item/specs/lastEntriesFilter.spec.js index 6c692848b..6db3d433e 100644 --- a/modules/item/back/methods/item/specs/lastEntriesFilter.spec.js +++ b/modules/item/back/methods/item/specs/lastEntriesFilter.spec.js @@ -1,6 +1,6 @@ const models = require('vn-loopback/server/server').models; -describe('item lastEntriesFilter()', () => { +fdescribe('item lastEntriesFilter()', () => { const minDate = new Date(value); minHour.setHours(0, 0, 0, 0); const maxDate = new Date(value); diff --git a/print/methods/notify/invocing.js b/print/methods/notify/invocing.js new file mode 100644 index 000000000..1172ef0ea --- /dev/null +++ b/print/methods/notify/invocing.js @@ -0,0 +1,45 @@ +const db = require('vn-print/core/database'); +const Email = require('vn-print/core/email'); + +module.exports = async function(request, response, next) { + try { + const reqArgs = request.body; + + if (!reqArgs.clientIds) + throw new Error('The argument clientIds is required'); + if (!reqArgs.from) + throw new Error('The argument from is required'); + if (!reqArgs.to) + throw new Error('The argument to is required'); + + response.status(200).json({ + message: 'Success' + }); + + const invoices = await db.rawSql(` + SELECT + * FROM invoiceOut_queue + WHERE status = 'PENDING'`); + + // const clientData = new Map(); + // for (const client of clients) + // clientData.set(client.id, client); + + // for (const clientId of reqArgs.clientIds) { + // const client = clientData.get(clientId); + + // if (client) { + // const args = Object.assign({ + // recipientId: clientId, + // recipient: client.email, + // replyTo: client.salesPersonEmail + // }, response.locals); + + // const email = new Email('campaign-metrics', args); + // await email.send(); + // } + // } + } catch (error) { + next(error); + } +}; -- 2.40.1 From fea7df269c0ac95bf4e9f43f241d4aeca286035a Mon Sep 17 00:00:00 2001 From: joan Date: Thu, 28 Apr 2022 14:59:29 +0200 Subject: [PATCH 2/5] Added invoice schedule method --- .../10451-april/00-invoiceOut_queue.sql | 2 +- .../methods/invoiceOut/globalInvoicing.js | 2 - print/methods/notify/invocing.js | 45 ------- print/methods/routes.js | 4 +- .../{notify => schedule}/consumption.js | 0 print/methods/{notify => schedule}/index.js | 1 + print/methods/schedule/invoice.js | 118 ++++++++++++++++++ 7 files changed, 122 insertions(+), 50 deletions(-) delete mode 100644 print/methods/notify/invocing.js rename print/methods/{notify => schedule}/consumption.js (100%) rename print/methods/{notify => schedule}/index.js (76%) create mode 100644 print/methods/schedule/invoice.js diff --git a/db/changes/10451-april/00-invoiceOut_queue.sql b/db/changes/10451-april/00-invoiceOut_queue.sql index 80f6aed40..0f91b809e 100644 --- a/db/changes/10451-april/00-invoiceOut_queue.sql +++ b/db/changes/10451-april/00-invoiceOut_queue.sql @@ -2,7 +2,7 @@ create table `vn`.`invoiceOut_queue` ( invoiceFk int(10) unsigned not null, dated datetime default now() null, - printed tinyint(1) default 0 null, + `status` VARCHAR(50) default '' null, constraint invoiceOut_queue_pk primary key (invoiceFk), constraint invoiceOut_queue_invoiceOut_id_fk diff --git a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js index f2c988909..c77021ff2 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js +++ b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js @@ -1,5 +1,3 @@ -const UserError = require('vn-loopback/util/user-error'); - module.exports = Self => { Self.remoteMethodCtx('globalInvoicing', { description: 'Make a global invoice', diff --git a/print/methods/notify/invocing.js b/print/methods/notify/invocing.js deleted file mode 100644 index 1172ef0ea..000000000 --- a/print/methods/notify/invocing.js +++ /dev/null @@ -1,45 +0,0 @@ -const db = require('vn-print/core/database'); -const Email = require('vn-print/core/email'); - -module.exports = async function(request, response, next) { - try { - const reqArgs = request.body; - - if (!reqArgs.clientIds) - throw new Error('The argument clientIds is required'); - if (!reqArgs.from) - throw new Error('The argument from is required'); - if (!reqArgs.to) - throw new Error('The argument to is required'); - - response.status(200).json({ - message: 'Success' - }); - - const invoices = await db.rawSql(` - SELECT - * FROM invoiceOut_queue - WHERE status = 'PENDING'`); - - // const clientData = new Map(); - // for (const client of clients) - // clientData.set(client.id, client); - - // for (const clientId of reqArgs.clientIds) { - // const client = clientData.get(clientId); - - // if (client) { - // const args = Object.assign({ - // recipientId: clientId, - // recipient: client.email, - // replyTo: client.salesPersonEmail - // }, response.locals); - - // const email = new Email('campaign-metrics', args); - // await email.send(); - // } - // } - } catch (error) { - next(error); - } -}; diff --git a/print/methods/routes.js b/print/methods/routes.js index ea40e0743..28671b3da 100644 --- a/print/methods/routes.js +++ b/print/methods/routes.js @@ -16,7 +16,7 @@ module.exports = [ cb: require('./closure') }, { - url: '/api/notify', - cb: require('./notify') + url: '/api/schedule', + cb: require('./schedule') } ]; diff --git a/print/methods/notify/consumption.js b/print/methods/schedule/consumption.js similarity index 100% rename from print/methods/notify/consumption.js rename to print/methods/schedule/consumption.js diff --git a/print/methods/notify/index.js b/print/methods/schedule/index.js similarity index 76% rename from print/methods/notify/index.js rename to print/methods/schedule/index.js index df4705d1e..05d54b2ed 100644 --- a/print/methods/notify/index.js +++ b/print/methods/schedule/index.js @@ -2,5 +2,6 @@ const express = require('express'); const router = new express.Router(); router.post('/consumption', require('./consumption')); +router.post('/invoice', require('./invoice')); module.exports = router; diff --git a/print/methods/schedule/invoice.js b/print/methods/schedule/invoice.js new file mode 100644 index 000000000..d27557d34 --- /dev/null +++ b/print/methods/schedule/invoice.js @@ -0,0 +1,118 @@ +const db = require('vn-print/core/database'); +const Email = require('vn-print/core/email'); +const Report = require('vn-print/core/report'); +const storage = require('vn-print/core/storage'); + +module.exports = async function(request, response, next) { + try { + const reqArgs = request.body; + + response.status(200).json({ + message: 'Success' + }); + + const invoices = await db.rawSql(` + SELECT + io.id, + io.clientFk, + io.issued, + io.ref, + c.email recipient, + c.salesPersonFk, + c.isToBeMailed, + c.hasToInvoice, + co.hasDailyInvoice, + eu.email salesPersonEmail + FROM invoiceOut_queue ioq + JOIN invoiceOut io ON io.id = ioq.invoiceFk + JOIN client c ON c.id = io.clientFk + JOIN province p ON p.id = c.provinceFk + JOIN country co ON co.id = p.countryFk + LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk + + WHERE status = ''`); + + for (const invoiceOut of invoices) { + try { + const args = Object.assign({ + invoiceId: invoiceOut.id, + recipientId: invoiceOut.clientFk, + recipient: invoiceOut.recipient, + replyTo: invoiceOut.salesPersonEmail + }, reqArgs); + + const invoiceReport = new Report('invoice', args); + const stream = await invoiceReport.toPdfStream(); + + const issued = invoiceOut.issued; + const year = issued.getFullYear().toString(); + const month = (issued.getMonth() + 1).toString(); + const day = issued.getDate().toString(); + + const fileName = `${year}${invoiceOut.ref}.pdf`; + + // Store invoice + storage.write(stream, { + type: 'invoice', + path: `${year}/${month}/${day}`, + fileName: fileName + }); + + await db.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id]); + + const isToBeMailed = invoiceOut.recipient && invoiceOut.salesPersonFk && invoiceOut.isToBeMailed; + + if (isToBeMailed) { + const mailOptions = { + overrideAttachments: true, + attachments: [] + }; + + const invoiceAttachment = { + filename: fileName, + content: stream + }; + + if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') { + const exportation = new Report('exportation', args); + const stream = await exportation.toPdfStream(); + const fileName = `CITES-${invoiceOut.ref}.pdf`; + + mailOptions.attachments.push({ + filename: fileName, + content: stream + }); + } + + mailOptions.attachments.push(invoiceAttachment); + + const email = new Email('invoice', args); + await email.send(mailOptions); + } + } catch (error) { + console.log(error); + } + } + + // const clientData = new Map(); + // for (const client of clients) + // clientData.set(client.id, client); + + // for (const clientId of reqArgs.clientIds) { + // const client = clientData.get(clientId); + + // if (client) { + // const args = Object.assign({ + // recipientId: clientId, + // recipient: client.email, + // replyTo: client.salesPersonEmail + // }, response.locals); + + // const email = new Email('campaign-metrics', args); + // await email.send(); + // } + // } + } catch (error) { + next(error); + } +}; -- 2.40.1 From ea6b1abdd88fc107c475b69d1b9c961f31d6c33a Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 2 May 2022 09:51:49 +0200 Subject: [PATCH 3/5] Invoice queue --- .../10451-april/00-invoiceOut_queue.sql | 3 +- .../methods/invoiceOut/globalInvoicing.js | 12 +++--- .../front/index/global-invoicing/index.html | 4 +- .../index/global-invoicing/locale/es.yml | 2 +- print/core/router.js | 3 ++ print/methods/schedule/invoice.js | 37 +++++++------------ 6 files changed, 28 insertions(+), 33 deletions(-) diff --git a/db/changes/10451-april/00-invoiceOut_queue.sql b/db/changes/10451-april/00-invoiceOut_queue.sql index 0f91b809e..f60bcab77 100644 --- a/db/changes/10451-april/00-invoiceOut_queue.sql +++ b/db/changes/10451-april/00-invoiceOut_queue.sql @@ -1,7 +1,8 @@ create table `vn`.`invoiceOut_queue` ( invoiceFk int(10) unsigned not null, - dated datetime default now() null, + queued datetime default now() not null, + printed datetime null, `status` VARCHAR(50) default '' null, constraint invoiceOut_queue_pk primary key (invoiceFk), diff --git a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js index c77021ff2..7f2cbb442 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js +++ b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js @@ -161,10 +161,6 @@ module.exports = Self => { throw e; } - // Print invoices PDF - // for (let invoiceId of invoicesIds) - // await Self.createPdf(ctx, invoiceId); - return invoicesIds; }; @@ -213,7 +209,13 @@ module.exports = Self => { const query = `SELECT c.id, - SUM(IFNULL(s.quantity * s.price * (100-s.discount)/100, 0) + IFNULL(ts.quantity * ts.price,0)) AS sumAmount, + 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, diff --git a/modules/invoiceOut/front/index/global-invoicing/index.html b/modules/invoiceOut/front/index/global-invoicing/index.html index 9fd412d0e..3d245b8d8 100644 --- a/modules/invoiceOut/front/index/global-invoicing/index.html +++ b/modules/invoiceOut/front/index/global-invoicing/index.html @@ -19,7 +19,7 @@ ng-if="$ctrl.isInvoicing"> - Invoicing in progress... + Adding invoices to queue... @@ -66,5 +66,5 @@ - + \ No newline at end of file diff --git a/modules/invoiceOut/front/index/global-invoicing/locale/es.yml b/modules/invoiceOut/front/index/global-invoicing/locale/es.yml index 51e165e2a..1a6e15656 100644 --- a/modules/invoiceOut/front/index/global-invoicing/locale/es.yml +++ b/modules/invoiceOut/front/index/global-invoicing/locale/es.yml @@ -1,7 +1,7 @@ Create global invoice: Crear factura global Some fields are required: Algunos campos son obligatorios Max date: Fecha límite -Invoicing in progress...: Facturación en progreso... +Adding invoices to queue...: Añadiendo facturas a la cola... Invoice date: Fecha de factura From client: Desde el cliente To client: Hasta el cliente diff --git a/print/core/router.js b/print/core/router.js index cd64ba07e..99f2b3ed6 100644 --- a/print/core/router.js +++ b/print/core/router.js @@ -15,6 +15,7 @@ module.exports = app => { WHERE at.id = ?`; const auth = await db.findOne(query, [token]); + console.log(auth); if (!auth || isTokenExpired(auth.created, auth.ttl)) throw new Error('Invalid authorization token'); @@ -31,6 +32,8 @@ module.exports = app => { locale: auth.lang }; + console.log(response.locals.auth); + next(); } catch (error) { next(error); diff --git a/print/methods/schedule/invoice.js b/print/methods/schedule/invoice.js index d27557d34..df4fe17b0 100644 --- a/print/methods/schedule/invoice.js +++ b/print/methods/schedule/invoice.js @@ -5,8 +5,6 @@ const storage = require('vn-print/core/storage'); module.exports = async function(request, response, next) { try { - const reqArgs = request.body; - response.status(200).json({ message: 'Success' }); @@ -29,17 +27,18 @@ module.exports = async function(request, response, next) { JOIN province p ON p.id = c.provinceFk JOIN country co ON co.id = p.countryFk LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk - WHERE status = ''`); + let invoiceId; for (const invoiceOut of invoices) { try { + invoiceId = invoiceOut.id; const args = Object.assign({ invoiceId: invoiceOut.id, recipientId: invoiceOut.clientFk, recipient: invoiceOut.recipient, replyTo: invoiceOut.salesPersonEmail - }, reqArgs); + }, response.locals); const invoiceReport = new Report('invoice', args); const stream = await invoiceReport.toPdfStream(); @@ -89,29 +88,19 @@ module.exports = async function(request, response, next) { const email = new Email('invoice', args); await email.send(mailOptions); } + // Update queue status + sql = `UPDATE invoiceOut_queue + SET status = "printed", + printed = NOW() + WHERE invoiceFk = ?`; + await db.rawSql(sql, [invoiceOut.id]); } catch (error) { - console.log(error); + sql = `UPDATE invoiceOut_queue + SET status = ? + WHERE invoiceFk = ?`; + await db.rawSql(sql, [error.message, invoiceId]); } } - - // const clientData = new Map(); - // for (const client of clients) - // clientData.set(client.id, client); - - // for (const clientId of reqArgs.clientIds) { - // const client = clientData.get(clientId); - - // if (client) { - // const args = Object.assign({ - // recipientId: clientId, - // recipient: client.email, - // replyTo: client.salesPersonEmail - // }, response.locals); - - // const email = new Email('campaign-metrics', args); - // await email.send(); - // } - // } } catch (error) { next(error); } -- 2.40.1 From d156cb1c30beb5f58be0cd21f2e54eeca3aeba75 Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 2 May 2022 13:52:57 +0200 Subject: [PATCH 4/5] Queue transaction --- print/core/router.js | 3 --- print/methods/schedule/invoice.js | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/print/core/router.js b/print/core/router.js index 99f2b3ed6..cd64ba07e 100644 --- a/print/core/router.js +++ b/print/core/router.js @@ -15,7 +15,6 @@ module.exports = app => { WHERE at.id = ?`; const auth = await db.findOne(query, [token]); - console.log(auth); if (!auth || isTokenExpired(auth.created, auth.ttl)) throw new Error('Invalid authorization token'); @@ -32,8 +31,6 @@ module.exports = app => { locale: auth.lang }; - console.log(response.locals.auth); - next(); } catch (error) { next(error); diff --git a/print/methods/schedule/invoice.js b/print/methods/schedule/invoice.js index df4fe17b0..c76ca85b5 100644 --- a/print/methods/schedule/invoice.js +++ b/print/methods/schedule/invoice.js @@ -29,10 +29,14 @@ module.exports = async function(request, response, next) { LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk WHERE status = ''`); + let connection; let invoiceId; for (const invoiceOut of invoices) { try { invoiceId = invoiceOut.id; + connection = await db.getConnection(); + connection.query('START TRANSACTION'); + const args = Object.assign({ invoiceId: invoiceOut.id, recipientId: invoiceOut.clientFk, @@ -57,7 +61,7 @@ module.exports = async function(request, response, next) { fileName: fileName }); - await db.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id]); + connection.query('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id]); const isToBeMailed = invoiceOut.recipient && invoiceOut.salesPersonFk && invoiceOut.isToBeMailed; @@ -93,8 +97,11 @@ module.exports = async function(request, response, next) { SET status = "printed", printed = NOW() WHERE invoiceFk = ?`; - await db.rawSql(sql, [invoiceOut.id]); + connection.query(sql, [invoiceOut.id]); + connection.query('COMMIT'); } catch (error) { + connection.query('ROLLBACK'); + connection.release(); sql = `UPDATE invoiceOut_queue SET status = ? WHERE invoiceFk = ?`; -- 2.40.1 From f806a598ea989ab9080da49f53728dcb0b2c19ab Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 9 May 2022 13:15:48 +0200 Subject: [PATCH 5/5] Updated method path --- modules/client/front/notification/index.js | 2 +- modules/client/front/notification/index.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/client/front/notification/index.js b/modules/client/front/notification/index.js index 12a1a4acb..0506ea4ba 100644 --- a/modules/client/front/notification/index.js +++ b/modules/client/front/notification/index.js @@ -81,7 +81,7 @@ export default class Controller extends Section { clientIds: clientIds }, this.campaign); - this.$http.post('notify/consumption', params) + this.$http.post('schedule/consumption', params) .then(() => this.$.filters.hide()) .then(() => this.vnApp.showSuccess(this.$t('Notifications sent!'))); } diff --git a/modules/client/front/notification/index.spec.js b/modules/client/front/notification/index.spec.js index 13c6bc513..8847357f7 100644 --- a/modules/client/front/notification/index.spec.js +++ b/modules/client/front/notification/index.spec.js @@ -74,7 +74,7 @@ describe('Client notification', () => { clientIds: [1101, 1102] }, controller.campaign); - $httpBackend.expect('POST', `notify/consumption`, params).respond(200, params); + $httpBackend.expect('POST', `schedule/consumption`, params).respond(200, params); controller.onSendClientConsumption(); $httpBackend.flush(); -- 2.40.1