diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js index b6e7e0261..7e80a4a7c 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.js +++ b/modules/invoiceOut/front/descriptor-menu/index.js @@ -19,6 +19,10 @@ class Controller extends Section { this.id = value.id; } + get hasInvoicing() { + return this.aclService.hasAny(['invoicing']); + } + loadData() { const filter = { include: [ diff --git a/print/core/router.js b/print/core/router.js index a8a68fb19..d42724b17 100644 --- a/print/core/router.js +++ b/print/core/router.js @@ -10,6 +10,10 @@ module.exports = app => { url: '/api/email', cb: require('../methods/email') }, + { + url: '/api/csv', + cb: require('../methods/csv') + }, { url: '/api/closure', cb: require('../methods/closure') diff --git a/print/core/smtp.js b/print/core/smtp.js index 36a76dbaf..50a413673 100644 --- a/print/core/smtp.js +++ b/print/core/smtp.js @@ -25,14 +25,17 @@ module.exports = { throw err; }).finally(async() => { const attachments = []; - for (let attachment of options.attachments) { - const fileName = attachment.filename; - const filePath = attachment.path; - if (fileName.includes('.png')) return; + if (options.attachments) { + for (let attachment of options.attachments) { + const fileName = attachment.filename; + const filePath = attachment.path; + if (fileName.includes('.png')) return; - if (fileName || filePath) - attachments.push(filePath ? filePath : fileName); + if (fileName || filePath) + attachments.push(filePath ? filePath : fileName); + } } + const fileNames = attachments.join(',\n'); await db.rawSql(` INSERT INTO vn.mail (receiver, replyTo, sent, subject, body, attachment, status) diff --git a/print/methods/closure/closure.js b/print/methods/closure/closure.js index d18b3fdd5..9e9ef54eb 100644 --- a/print/methods/closure/closure.js +++ b/print/methods/closure/closure.js @@ -12,6 +12,8 @@ module.exports = { const failedtickets = []; for (const ticket of tickets) { try { + await db.rawSql('START TRANSACTION'); + await db.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id]); const invoiceOut = await db.findOne(` @@ -54,6 +56,8 @@ module.exports = { fileName: fileName }); + await db.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id]); + if (isToBeMailed) { const invoiceAttachment = { filename: fileName, @@ -87,7 +91,9 @@ module.exports = { const email = new Email('delivery-note-link', args); await email.send(); } + await db.rawSql('COMMIT'); } catch (error) { + await db.rawSql('ROLLBACK'); // Domain not found if (error.responseCode == 450) return invalidEmail(ticket); diff --git a/print/methods/closure/closure_old.js b/print/methods/closure/closure_old.js deleted file mode 100644 index 2b42982cd..000000000 --- a/print/methods/closure/closure_old.js +++ /dev/null @@ -1,346 +0,0 @@ -const db = require('../core/database'); -const Email = require('../core/email'); -const Report = require('../core/report'); -const smtp = require('../core/smtp'); -const config = require('../core/config'); -const storage = require('../core/storage'); - -module.exports = app => { - app.get('/api/closure/all', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.to) - throw new Error('The argument to is required'); - - res.status(200).json({ - message: 'Task executed successfully' - }); - - const tickets = await db.rawSql(` - SELECT - t.id - FROM ticket t - JOIN agencyMode am ON am.id = t.agencyModeFk - JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission - JOIN ticketState ts ON ts.ticketFk = t.id - JOIN alertLevel al ON al.id = ts.alertLevel - WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code != 'delivered')) - AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY) - AND util.dayEnd(?) - AND t.refFk IS NULL - GROUP BY t.id`, [reqArgs.to, reqArgs.to]); - const ticketIds = tickets.map(ticket => ticket.id); - - await closeAll(ticketIds, req.args); - await db.rawSql(` - UPDATE ticket t - JOIN ticketState ts ON t.id = ts.ticketFk - JOIN alertLevel al ON al.id = ts.alertLevel - JOIN agencyMode am ON am.id = t.agencyModeFk - JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk - JOIN zone z ON z.id = t.zoneFk - SET t.routeFk = NULL - WHERE DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY) - AND util.dayEnd(?) - AND al.code NOT IN('DELIVERED','PACKED') - AND t.routeFk - AND z.name LIKE '%MADRID%'`, [reqArgs.to, reqArgs.to]); - } catch (error) { - next(error); - } - }); - - app.get('/api/closure/by-ticket', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.ticketId) - throw new Error('The argument ticketId is required'); - - res.status(200).json({ - message: 'Task executed successfully' - }); - - const tickets = await db.rawSql(` - SELECT - t.id - FROM expedition e - JOIN ticket t ON t.id = e.ticketFk - JOIN ticketState ts ON ts.ticketFk = t.id - JOIN alertLevel al ON al.id = ts.alertLevel - WHERE al.code = 'PACKED' - AND t.id = ? - AND t.refFk IS NULL - GROUP BY e.ticketFk`, [reqArgs.ticketId]); - const ticketIds = tickets.map(ticket => ticket.id); - - await closeAll(ticketIds, reqArgs); - } catch (error) { - next(error); - } - }); - - app.get('/api/closure/by-agency', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.agencyModeId) - throw new Error('The argument agencyModeId is required'); - - if (!reqArgs.warehouseId) - throw new Error('The argument warehouseId is required'); - - if (!reqArgs.to) - throw new Error('The argument to is required'); - - res.status(200).json({ - message: 'Task executed successfully' - }); - - const agenciesId = reqArgs.agencyModeId.split(','); - const tickets = await db.rawSql(` - SELECT - t.id - FROM expedition e - JOIN ticket t ON t.id = e.ticketFk - JOIN ticketState ts ON ts.ticketFk = t.id - JOIN alertLevel al ON al.id = ts.alertLevel - WHERE al.code = 'PACKED' - AND t.agencyModeFk IN(?) - AND t.warehouseFk = ? - AND DATE(t.shipped) BETWEEN DATE_ADD(:to, INTERVAL -2 DAY) - AND util.dayEnd(?) - AND t.refFk IS NULL - GROUP BY e.ticketFk`, [ - agenciesId, - reqArgs.warehouseId, - reqArgs.to, - reqArgs.to - ]); - const ticketIds = tickets.map(ticket => ticket.id); - - await closeAll(ticketIds, reqArgs); - } catch (error) { - next(error); - } - }); - - app.get('/api/closure/by-route', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.routeId) - throw new Error('The argument routeId is required'); - - res.status(200).json({ - message: 'Task executed successfully' - }); - - const tickets = await db.rawSql(` - SELECT - t.id - FROM expedition e - JOIN ticket t ON t.id = e.ticketFk - JOIN ticketState ts ON ts.ticketFk = t.id - JOIN alertLevel al ON al.id = ts.alertLevel - WHERE al.code = 'PACKED' - AND t.routeFk = ? - AND t.refFk IS NULL - GROUP BY e.ticketFk`, [reqArgs.routeId]); - const ticketIds = tickets.map(ticket => ticket.id); - await closeAll(ticketIds, reqArgs); - - // Send route report to the agency - const agencyMail = await db.findValue(` - SELECT am.reportMail - FROM route r - JOIN agencyMode am ON am.id = r.agencyModeFk - WHERE r.id = ?`, [reqArgs.routeId]); - - if (agencyMail) { - const args = Object.assign({ - routeId: reqArgs.routeId, - recipient: agencyMail - }, reqArgs); - - const email = new Email('driver-route', args); - await email.send(); - } - } catch (error) { - next(error); - } - }); - - async function closeAll(ticketIds, reqArgs) { - if (ticketIds.length == 0) return; - const failedtickets = []; - const tickets = await db.rawSql(` - SELECT - t.id, - t.clientFk, - c.name clientName, - c.email recipient, - c.salesPersonFk, - c.isToBeMailed, - c.hasToInvoice, - co.hasDailyInvoice, - eu.email salesPersonEmail - FROM ticket t - JOIN client c ON c.id = t.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 t.id IN(?)`, [ticketIds]); - - for (const ticket of tickets) { - try { - await db.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id]); - - const invoiceOut = await db.findOne(` - SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued - FROM ticket t - JOIN invoiceOut io ON io.ref = t.refFk - JOIN company cny ON cny.id = io.companyFk - WHERE t.id = ? - `, [ticket.id]); - - const mailOptions = { - overrideAttachments: true, - attachments: [] - }; - - const args = Object.assign({ - invoiceId: invoiceOut.id, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }, reqArgs); - - if (invoiceOut) { - 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`; - - storage.write(stream, { - type: 'invoice', - path: `${year}/${month}/${day}`, - fileName: fileName - }); - - mailOptions.attachments.push({ - filename: fileName, - content: stream - }); - } - - if (!ticket.salesPersonFk || !ticket.isToBeMailed) continue; - - if (!ticket.recipient) { - const body = `No se ha podido enviar el albarán ${ticket.id} - al cliente ${ticket.clientFk} porque no tiene un email especificado.

- Para dejar de recibir esta notificación, asígnale un email o desactiva - la notificación por email para este cliente.`; - smtp.send({ - to: ticket.salesPersonEmail, - subject: 'No se ha podido enviar el albarán', - html: body - }); - - continue; - } - - const hasToInvoice = ticket.hasToInvoice && ticket.hasDailyInvoice; - if (hasToInvoice) { - /* const args = Object.assign({ - invoiceId: invoiceOut.id, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }, reqArgs); - - */ - 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 - }); - } - - const email = new Email('invoice', args); - await email.send(mailOptions); - } else { - const args = Object.assign({ - ticketId: ticket.id, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }, reqArgs); - - const email = new Email('delivery-note-link', args); - await email.send(); - } - } catch (error) { - // Domain not found - if (error.responseCode == 450) - return invalidEmail(ticket); - - // Save tickets on a list of failed ids - failedtickets.push({ - id: ticket.id, - stacktrace: error - }); - } - } - - // Send email with failed tickets - if (failedtickets.length > 0) { - let body = 'This following tickets have failed:

'; - - for (ticket of failedtickets) { - body += `Ticket: ${ticket.id} -
${ticket.stacktrace}

`; - } - - smtp.send({ - to: config.app.reportEmail, - subject: '[API] Nightly ticket closure report', - html: body - }); - } - } - - async function invalidEmail(ticket) { - await db.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [ - ticket.clientFk - ]); - - const oldInstance = `{"email": "${ticket.recipient}"}`; - const newInstance = `{"email": ""}`; - await db.rawSql(` - INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance) - VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [ - ticket.clientFk, - oldInstance, - newInstance - ]); - - const body = `No se ha podido enviar el albarán ${ticket.id} - al cliente ${ticket.clientFk} - ${ticket.clientName} - porque la dirección de email "${ticket.recipient}" no es correcta o no está disponible.

- Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente. - Actualiza la dirección de email con una correcta.`; - - smtp.send({ - to: ticket.salesPersonEmail, - subject: 'No se ha podido enviar el albarán', - html: body - }); - } -}; diff --git a/print/methods/csv/csv.js b/print/methods/csv/csv.js index 4f4cdf2af..d8725582d 100644 --- a/print/methods/csv/csv.js +++ b/print/methods/csv/csv.js @@ -1,31 +1,31 @@ -module.exports = app => { - app.use('/api/csv/delivery-note', require('./csv/delivery-note')(app)); - app.use('/api/csv/invoice', require('./csv/invoice')(app)); - - app.toCSV = function toCSV(rows) { - const [columns] = rows; - let content = Object.keys(columns).join('\t'); - for (let row of rows) { - const values = Object.values(row); - const finalValues = values.map(value => { - if (value instanceof Date) return formatDate(value); - if (value === null) return ''; - return value; - }); - content += '\n'; - content += finalValues.join('\t'); - } - return content; - }; - - function formatDate(date) { - return new Intl.DateTimeFormat('es', { - year: 'numeric', - month: 'numeric', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - }).format(date); +function toCSV(rows) { + const [columns] = rows; + let content = Object.keys(columns).join('\t'); + for (let row of rows) { + const values = Object.values(row); + const finalValues = values.map(value => { + if (value instanceof Date) return formatDate(value); + if (value === null) return ''; + return value; + }); + content += '\n'; + content += finalValues.join('\t'); } + return content; +} + +function formatDate(date) { + return new Intl.DateTimeFormat('es', { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }).format(date); +} + +module.exports = { + toCSV, + formatDate }; diff --git a/print/methods/csv/delivery-note/download.js b/print/methods/csv/delivery-note/download.js new file mode 100644 index 000000000..d369d5f4a --- /dev/null +++ b/print/methods/csv/delivery-note/download.js @@ -0,0 +1,24 @@ +const path = require('path'); +const db = require('vn-print/core/database'); + +const {toCSV} = require('../csv'); +const sqlPath = path.join(__dirname, 'sql'); + +module.exports = async function(request, response, next) { + try { + const reqArgs = request.query; + if (!reqArgs.ticketId) + throw new Error('The argument ticketId is required'); + + const ticketId = reqArgs.ticketId; + const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]); + const content = toCSV(sales); + const fileName = `ticket_${ticketId}.csv`; + + response.setHeader('Content-type', 'text/csv'); + response.setHeader('Content-Disposition', `inline; filename="${fileName}"`); + response.end(content); + } catch (error) { + next(error); + } +}; diff --git a/print/methods/csv/delivery-note/index.js b/print/methods/csv/delivery-note/index.js deleted file mode 100644 index 9ef0e33fa..000000000 --- a/print/methods/csv/delivery-note/index.js +++ /dev/null @@ -1,82 +0,0 @@ -const express = require('express'); -const router = new express.Router(); -const path = require('path'); -const db = require('../../../core/database'); -const sqlPath = path.join(__dirname, 'sql'); - -module.exports = app => { - router.get('/preview', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.ticketId) - throw new Error('The argument ticketId is required'); - - const ticketId = reqArgs.ticketId; - const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]); - const content = app.toCSV(sales); - const fileName = `ticket_${ticketId}.csv`; - - res.setHeader('Content-type', 'application/json; charset=utf-8'); - res.setHeader('Content-Disposition', `inline; filename="${fileName}"`); - res.end(content); - } catch (error) { - next(error); - } - }); - - router.get('/download', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.ticketId) - throw new Error('The argument ticketId is required'); - - const ticketId = reqArgs.ticketId; - const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]); - const content = app.toCSV(sales); - const fileName = `ticket_${ticketId}.csv`; - - res.setHeader('Content-type', 'text/csv'); - res.setHeader('Content-Disposition', `inline; filename="${fileName}"`); - res.end(content); - } catch (error) { - next(error); - } - }); - - const Email = require('../../../core/email'); - router.get('/send', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.ticketId) - throw new Error('The argument ticketId is required'); - - const ticketId = reqArgs.ticketId; - const ticket = await db.findOneFromDef(`${sqlPath}/ticket`, [ticketId]); - const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]); - - const args = Object.assign({ - ticketId: (String(ticket.id)), - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }, reqArgs); - - const content = app.toCSV(sales); - const fileName = `ticket_${ticketId}.csv`; - const email = new Email('delivery-note', args); - await email.send({ - overrideAttachments: true, - attachments: [{ - filename: fileName, - content: content - }] - }); - - res.status(200).json({message: 'ok'}); - } catch (error) { - next(error); - } - }); - - return router; -}; diff --git a/print/methods/csv/delivery-note/send.js b/print/methods/csv/delivery-note/send.js new file mode 100644 index 000000000..478f20f57 --- /dev/null +++ b/print/methods/csv/delivery-note/send.js @@ -0,0 +1,40 @@ +const path = require('path'); +const db = require('vn-print/core/database'); +const Email = require('vn-print/core/email'); + +const {toCSV} = require('../csv'); +const sqlPath = path.join(__dirname, 'sql'); + +module.exports = async function(request, response, next) { + try { + const reqArgs = request.query; + if (!reqArgs.ticketId) + throw new Error('The argument ticketId is required'); + + const ticketId = reqArgs.ticketId; + const ticket = await db.findOneFromDef(`${sqlPath}/ticket`, [ticketId]); + const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]); + + const args = Object.assign({ + ticketId: (String(ticket.id)), + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail + }, response.locals); + + const content = toCSV(sales); + const fileName = `ticket_${ticketId}.csv`; + const email = new Email('delivery-note', args); + await email.send({ + overrideAttachments: true, + attachments: [{ + filename: fileName, + content: content + }] + }); + + response.status(200).json({message: 'Success'}); + } catch (error) { + next(error); + } +}; diff --git a/print/methods/csv/index.js b/print/methods/csv/index.js new file mode 100644 index 000000000..6bdd1b60d --- /dev/null +++ b/print/methods/csv/index.js @@ -0,0 +1,9 @@ +const express = require('express'); +const router = new express.Router(); + +router.get('/delivery-note/download', require('./delivery-note/download')); +router.get('/delivery-note/send', require('./delivery-note/send')); +router.get('/invoice/download', require('./invoice/download')); +router.get('/invoice/send', require('./invoice/send')); + +module.exports = router; diff --git a/print/methods/csv/invoice/download.js b/print/methods/csv/invoice/download.js new file mode 100644 index 000000000..593d2d8d0 --- /dev/null +++ b/print/methods/csv/invoice/download.js @@ -0,0 +1,24 @@ +const path = require('path'); +const db = require('vn-print/core/database'); + +const {toCSV} = require('../csv'); +const sqlPath = path.join(__dirname, 'sql'); + +module.exports = async function(request, response, next) { + try { + const reqArgs = request.query; + if (!reqArgs.invoiceId) + throw new Error('The argument invoiceId is required'); + + const invoiceId = reqArgs.invoiceId; + const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]); + const content = toCSV(sales); + const fileName = `invoice_${invoiceId}.csv`; + + response.setHeader('Content-type', 'text/csv'); + response.setHeader('Content-Disposition', `inline; filename="${fileName}"`); + response.end(content); + } catch (error) { + next(error); + } +}; diff --git a/print/methods/csv/invoice/index.js b/print/methods/csv/invoice/index.js deleted file mode 100644 index 8f325be02..000000000 --- a/print/methods/csv/invoice/index.js +++ /dev/null @@ -1,82 +0,0 @@ -const express = require('express'); -const router = new express.Router(); -const path = require('path'); -const db = require('../../../core/database'); -const sqlPath = path.join(__dirname, 'sql'); - -module.exports = app => { - router.get('/preview', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.invoiceId) - throw new Error('The argument invoiceId is required'); - - const invoiceId = reqArgs.invoiceId; - const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]); - const content = app.toCSV(sales); - const fileName = `invoice_${invoiceId}.csv`; - - res.setHeader('Content-type', 'application/json; charset=utf-8'); - res.setHeader('Content-Disposition', `inline; filename="${fileName}"`); - res.end(content); - } catch (error) { - next(error); - } - }); - - router.get('/download', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.invoiceId) - throw new Error('The argument invoiceId is required'); - - const invoiceId = reqArgs.invoiceId; - const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]); - const content = app.toCSV(sales); - const fileName = `invoice_${invoiceId}.csv`; - - res.setHeader('Content-type', 'text/csv'); - res.setHeader('Content-Disposition', `inline; filename="${fileName}"`); - res.end(content); - } catch (error) { - next(error); - } - }); - - const Email = require('../../../core/email'); - router.get('/send', async function(req, res, next) { - try { - const reqArgs = req.args; - if (!reqArgs.invoiceId) - throw new Error('The argument invoiceId is required'); - - const invoiceId = reqArgs.invoiceId; - const invoice = await db.findOneFromDef(`${sqlPath}/invoice`, [invoiceId]); - const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]); - - const args = Object.assign({ - invoiceId: (String(invoice.id)), - recipientId: invoice.clientFk, - recipient: invoice.recipient, - replyTo: invoice.salesPersonEmail - }, reqArgs); - - const content = app.toCSV(sales); - const fileName = `invoice_${invoiceId}.csv`; - const email = new Email('invoice', args); - await email.send({ - overrideAttachments: true, - attachments: [{ - filename: fileName, - content: content - }] - }); - - res.status(200).json({message: 'ok'}); - } catch (error) { - next(error); - } - }); - - return router; -}; diff --git a/print/methods/csv/invoice/send.js b/print/methods/csv/invoice/send.js new file mode 100644 index 000000000..919d7aeb1 --- /dev/null +++ b/print/methods/csv/invoice/send.js @@ -0,0 +1,40 @@ +const path = require('path'); +const db = require('vn-print/core/database'); +const Email = require('vn-print/core/email'); + +const {toCSV} = require('../csv'); +const sqlPath = path.join(__dirname, 'sql'); + +module.exports = async function(request, response, next) { + try { + const reqArgs = request.query; + if (!reqArgs.invoiceId) + throw new Error('The argument invoiceId is required'); + + const invoiceId = reqArgs.invoiceId; + const invoice = await db.findOneFromDef(`${sqlPath}/invoice`, [invoiceId]); + const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]); + + const args = Object.assign({ + invoiceId: (String(invoice.id)), + recipientId: invoice.clientFk, + recipient: invoice.recipient, + replyTo: invoice.salesPersonEmail + }, response.locals); + + const content = toCSV(sales); + const fileName = `invoice_${invoiceId}.csv`; + const email = new Email('invoice', args); + await email.send({ + overrideAttachments: true, + attachments: [{ + filename: fileName, + content: content + }] + }); + + response.status(200).json({message: 'Success'}); + } catch (error) { + next(error); + } +};