From 831d358e41aae03ceb29ff46b1bf8b469c1adb35 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 15 Jan 2024 10:52:02 +0100 Subject: [PATCH 1/7] refs #6174 feat first approach --- .../invoiceOut/back/methods/invoiceOut/transferInvoice.js | 8 ++++---- modules/ticket/back/methods/ticket/invoiceTickets.js | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js index 782eaf6d8..3372c84dd 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js @@ -50,6 +50,7 @@ module.exports = Self => { Self.transferInvoice = async(ctx, options) => { const models = Self.app.models; const myOptions = {userId: ctx.req.accessToken.userId}; + let makePdf = false; const {id, refFk, newClientFk, cplusRectificationTypeFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk} = ctx.args; let tx; if (typeof options == 'object') @@ -63,6 +64,7 @@ module.exports = Self => { if (!myOptions.transaction) { tx = await Self.beginTransaction({}); myOptions.transaction = tx; + makePdf = true; } try { const filterRef = {where: {refFk: refFk}}; @@ -93,11 +95,9 @@ module.exports = Self => { await models.Ticket.invoiceTickets(ctx, refundTicketIds, invoiceCorrection, myOptions); const [invoiceId] = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, null, myOptions); - if (tx) { - await tx.commit(); - await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null); - } + tx && await tx.commit(); + makePdf && await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null); return invoiceId; } catch (e) { if (tx) await tx.rollback(); diff --git a/modules/ticket/back/methods/ticket/invoiceTickets.js b/modules/ticket/back/methods/ticket/invoiceTickets.js index 06429836e..162ddda03 100644 --- a/modules/ticket/back/methods/ticket/invoiceTickets.js +++ b/modules/ticket/back/methods/ticket/invoiceTickets.js @@ -32,6 +32,7 @@ module.exports = function(Self) { const models = Self.app.models; const date = Date.vnNew(); date.setHours(0, 0, 0, 0); + let makePdf = false; const myOptions = {userId: ctx.req.accessToken.userId}; let tx; @@ -42,6 +43,7 @@ module.exports = function(Self) { if (!myOptions.transaction) { tx = await Self.beginTransaction({}); myOptions.transaction = tx; + makePdf = true; } let invoicesIds = []; @@ -81,7 +83,7 @@ module.exports = function(Self) { if (tx) await tx.rollback(); throw e; } - if (tx) { + if (makePdf) { for (const invoiceId of invoicesIds) await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null); } From 21ce57d21701d99a2446be7de0c1d791f9e061ea Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 17 Jan 2024 10:31:07 +0100 Subject: [PATCH 2/7] refs #6174 feat: update back to separate invoice and pdf --- .../back/methods/invoiceOut/makePdfList.js | 30 +++++++++++++ .../methods/invoiceOut/transferInvoice.js | 14 +++--- modules/invoiceOut/back/models/invoice-out.js | 1 + .../back/methods/ticket/invoiceTickets.js | 8 +--- .../methods/ticket/invoiceTicketsAndPdf.js | 44 +++++++++++++++++++ .../ticket/specs/invoiceTickets.spec.js | 2 +- modules/ticket/back/models/ticket-methods.js | 1 + 7 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 modules/invoiceOut/back/methods/invoiceOut/makePdfList.js create mode 100644 modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js diff --git a/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js b/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js new file mode 100644 index 000000000..cf041ee17 --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js @@ -0,0 +1,30 @@ + +module.exports = Self => { + Self.remoteMethodCtx('makePdfList', { + description: 'Handle a list of invoices to generate PDF and send it to client', + accessType: 'WRITE', + accepts: [ + { + arg: 'ids', + type: ['number'], + description: 'The invoice id', + required: true, + http: {source: 'path'} + }, { + arg: 'printerFk', + type: 'number', + description: 'The printer to print' + } + ], + http: { + path: '/:id/makePdfList', + verb: 'POST' + } + }); + + Self.makePdfList = async function(ctx, ids, printerFk, options) { + ids.forEach(async id => { + await Self.makePdfAndNotify(ctx, id, printerFk, options); + }); + }; +}; diff --git a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js index 3372c84dd..8e234d7cc 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js @@ -50,7 +50,6 @@ module.exports = Self => { Self.transferInvoice = async(ctx, options) => { const models = Self.app.models; const myOptions = {userId: ctx.req.accessToken.userId}; - let makePdf = false; const {id, refFk, newClientFk, cplusRectificationTypeFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk} = ctx.args; let tx; if (typeof options == 'object') @@ -64,7 +63,6 @@ module.exports = Self => { if (!myOptions.transaction) { tx = await Self.beginTransaction({}); myOptions.transaction = tx; - makePdf = true; } try { const filterRef = {where: {refFk: refFk}}; @@ -88,16 +86,18 @@ module.exports = Self => { clonedTicketIds.push(clonedTicket.id); } - const invoiceCorrection = - {correctedFk: id, cplusRectificationTypeFk, siiTypeInvoiceOutFk, invoiceCorrectionTypeFk}; + const invoiceCorrection = { + correctedFk: id, + cplusRectificationTypeFk, + siiTypeInvoiceOutFk, + invoiceCorrectionTypeFk + }; const refundTicketIds = refundTickets.map(ticket => ticket.id); await models.Ticket.invoiceTickets(ctx, refundTicketIds, invoiceCorrection, myOptions); - const [invoiceId] = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, null, myOptions); - tx && await tx.commit(); + const [invoiceId] = await models.Ticket.invoiceTicketsAndPdf(ctx, clonedTicketIds, null, myOptions); - makePdf && await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null); return invoiceId; } catch (e) { if (tx) await tx.rollback(); diff --git a/modules/invoiceOut/back/models/invoice-out.js b/modules/invoiceOut/back/models/invoice-out.js index ca77c856f..91f4883ad 100644 --- a/modules/invoiceOut/back/models/invoice-out.js +++ b/modules/invoiceOut/back/models/invoice-out.js @@ -13,6 +13,7 @@ module.exports = Self => { require('../methods/invoiceOut/createManualInvoice')(Self); require('../methods/invoiceOut/clientsToInvoice')(Self); require('../methods/invoiceOut/invoiceClient')(Self); + require('../methods/invoiceOut/makePdfList')(Self); require('../methods/invoiceOut/makePdfAndNotify')(Self); require('../methods/invoiceOut/refund')(Self); require('../methods/invoiceOut/invoiceEmail')(Self); diff --git a/modules/ticket/back/methods/ticket/invoiceTickets.js b/modules/ticket/back/methods/ticket/invoiceTickets.js index 162ddda03..53400e724 100644 --- a/modules/ticket/back/methods/ticket/invoiceTickets.js +++ b/modules/ticket/back/methods/ticket/invoiceTickets.js @@ -32,7 +32,6 @@ module.exports = function(Self) { const models = Self.app.models; const date = Date.vnNew(); date.setHours(0, 0, 0, 0); - let makePdf = false; const myOptions = {userId: ctx.req.accessToken.userId}; let tx; @@ -43,7 +42,6 @@ module.exports = function(Self) { if (!myOptions.transaction) { tx = await Self.beginTransaction({}); myOptions.transaction = tx; - makePdf = true; } let invoicesIds = []; @@ -78,15 +76,11 @@ module.exports = function(Self) { for (const ticketIds of ticketsByAddress) invoicesIds.push(await createInvoice(ctx, companyId, ticketIds, invoiceCorrection, myOptions)); - if (tx) await tx.commit(); + tx && await tx.commit(); } catch (e) { if (tx) await tx.rollback(); throw e; } - if (makePdf) { - for (const invoiceId of invoicesIds) - await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null); - } return invoicesIds; }; diff --git a/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js new file mode 100644 index 000000000..3831854a7 --- /dev/null +++ b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js @@ -0,0 +1,44 @@ + +module.exports = function(Self) { + Self.remoteMethodCtx('invoiceTicketsAndPdf', { + description: 'Make out an invoice from one or more tickets', + accessType: 'WRITE', + accepts: [ + { + arg: 'ticketsIds', + description: 'The tickets id', + type: ['number'], + required: true + }, + { + arg: 'invoiceCorrection', + description: 'The invoice correction', + type: 'object', + } + + ], + returns: { + type: ['object'], + root: true + }, + http: { + path: `/invoiceTicketsAndPdf`, + verb: 'POST' + } + }); + + Self.invoiceTicketsAndPdf = async(ctx, ticketsIds, invoiceCorrection, options) => { + let invoiceIds = null; + + try { + invoiceIds = await Self.invoiceTickets(ctx, ticketsIds, invoiceCorrection, options); + if (!invoiceIds) return []; + if (!options.transaction) + await Self.app.models.InvoiceOut.makePdfList(ctx, invoiceIds, null, options); + return invoiceIds; + } catch (error) { + return []; + } + }; +}; + diff --git a/modules/ticket/back/methods/ticket/specs/invoiceTickets.spec.js b/modules/ticket/back/methods/ticket/specs/invoiceTickets.spec.js index 162dc066a..7d511cfd2 100644 --- a/modules/ticket/back/methods/ticket/specs/invoiceTickets.spec.js +++ b/modules/ticket/back/methods/ticket/specs/invoiceTickets.spec.js @@ -102,7 +102,7 @@ describe('ticket invoiceTickets()', () => { const options = {transaction: tx}; const ticketsIds = [11]; - const invoicesIds = await models.Ticket.invoiceTickets(ctx, ticketsIds, null, options); + const invoicesIds = await models.Ticket.invoiceTicketsAndPdf(ctx, ticketsIds, null, options); expect(invoicesIds.length).toBeGreaterThan(0); diff --git a/modules/ticket/back/models/ticket-methods.js b/modules/ticket/back/models/ticket-methods.js index 14cb104be..ce54959e7 100644 --- a/modules/ticket/back/models/ticket-methods.js +++ b/modules/ticket/back/models/ticket-methods.js @@ -42,5 +42,6 @@ module.exports = function(Self) { require('../methods/ticket/expeditionPalletLabel')(Self); require('../methods/ticket/saveSign')(Self); require('../methods/ticket/invoiceTickets')(Self); + require('../methods/ticket/invoiceTicketsAndPdf')(Self); require('../methods/ticket/docuwareDownload')(Self); }; From bc6f9bc0744d13d90f2a93746a40f53838aa0602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Segarra=20Mart=C3=ADnez?= Date: Tue, 23 Jan 2024 23:52:45 +0100 Subject: [PATCH 3/7] refs #6174 test: new test for new method --- .../back/methods/invoiceOut/makePdfList.js | 7 +- .../methods/ticket/invoiceTicketsAndPdf.js | 14 +-- .../ticket/specs/invoiceTicketsAndPdf.spec.js | 115 ++++++++++++++++++ 3 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 modules/ticket/back/methods/ticket/specs/invoiceTicketsAndPdf.spec.js diff --git a/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js b/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js index cf041ee17..a0449aeda 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js +++ b/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js @@ -23,8 +23,9 @@ module.exports = Self => { }); Self.makePdfList = async function(ctx, ids, printerFk, options) { - ids.forEach(async id => { - await Self.makePdfAndNotify(ctx, id, printerFk, options); - }); + const pdfs = ids.map(id => + Self.makePdfAndNotify(ctx, id, printerFk, options) + ); + await Promise.all(pdfs); }; }; diff --git a/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js index 3831854a7..5529a3ae3 100644 --- a/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js +++ b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js @@ -30,15 +30,11 @@ module.exports = function(Self) { Self.invoiceTicketsAndPdf = async(ctx, ticketsIds, invoiceCorrection, options) => { let invoiceIds = null; - try { - invoiceIds = await Self.invoiceTickets(ctx, ticketsIds, invoiceCorrection, options); - if (!invoiceIds) return []; - if (!options.transaction) - await Self.app.models.InvoiceOut.makePdfList(ctx, invoiceIds, null, options); - return invoiceIds; - } catch (error) { - return []; - } + invoiceIds = await Self.invoiceTickets(ctx, ticketsIds, invoiceCorrection, options); + if (!invoiceIds) return []; + if (!options.transaction) + await Self.app.models.InvoiceOut.makePdfList(ctx, invoiceIds, null, options); + return invoiceIds; }; }; diff --git a/modules/ticket/back/methods/ticket/specs/invoiceTicketsAndPdf.spec.js b/modules/ticket/back/methods/ticket/specs/invoiceTicketsAndPdf.spec.js new file mode 100644 index 000000000..086d1117f --- /dev/null +++ b/modules/ticket/back/methods/ticket/specs/invoiceTicketsAndPdf.spec.js @@ -0,0 +1,115 @@ +const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); + +describe('ticket invoiceTicketsAndPdf()', () => { + const userId = 19; + const clientId = 1102; + const activeCtx = { + getLocale: () => { + return 'en'; + }, + accessToken: {userId: userId}, + headers: {origin: 'http://localhost:5000'}, + }; + const ctx = {req: activeCtx}; + + beforeAll(async() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + it('should throw an error when invoicing tickets from multiple clients', async() => { + const invoiceOutModel = models.InvoiceOut; + spyOn(invoiceOutModel, 'makePdfAndNotify'); + + const tx = await models.Ticket.beginTransaction({}); + + let error; + + try { + const options = {transaction: tx}; + + const ticketsIds = [11, 16]; + await models.Ticket.invoiceTicketsAndPdf(ctx, ticketsIds, null, options); + + await tx.rollback(); + } catch (e) { + error = e; + await tx.rollback(); + } + + expect(error.message).toEqual(`You can't invoice tickets from multiple clients`); + }); + + it(`should throw an error when invoicing a client without tax data checked`, async() => { + const invoiceOutModel = models.InvoiceOut; + spyOn(invoiceOutModel, 'makePdfAndNotify'); + + const tx = await models.Ticket.beginTransaction({}); + + let error; + + try { + const options = {transaction: tx}; + + const client = await models.Client.findById(clientId, null, options); + await client.updateAttribute('isTaxDataChecked', false, options); + + const ticketsIds = [11]; + await models.Ticket.invoiceTicketsAndPdf(ctx, ticketsIds, null, options); + + await tx.rollback(); + } catch (e) { + error = e; + await tx.rollback(); + } + + expect(error.message).toEqual(`This client can't be invoiced`); + }); + + it('should invoice a ticket, then try again to fail', async() => { + const invoiceOutModel = models.InvoiceOut; + spyOn(invoiceOutModel, 'makePdfAndNotify'); + + const tx = await models.Ticket.beginTransaction({}); + + let error; + + try { + const options = {transaction: tx}; + + const ticketsIds = [11]; + await models.Ticket.invoiceTicketsAndPdf(ctx, ticketsIds, null, options); + await models.Ticket.invoiceTicketsAndPdf(ctx, ticketsIds, null, options); + + await tx.rollback(); + } catch (e) { + error = e; + await tx.rollback(); + } + + expect(error.message).toEqual(`This ticket is already invoiced`); + }); + + it('should success to invoice a ticket', async() => { + const invoiceOutModel = models.InvoiceOut; + spyOn(invoiceOutModel, 'makePdfAndNotify'); + + const tx = await models.Ticket.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const ticketsIds = [11]; + const invoicesIds = await models.Ticket.invoiceTicketsAndPdf(ctx, ticketsIds, null, options); + + expect(invoicesIds.length).toBeGreaterThan(0); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); From 0afc9ab1095f3300c6441c7ce3df80e14a5d5046 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 26 Jan 2024 09:55:21 +0100 Subject: [PATCH 4/7] refs #6174 test: fix test bug --- .../back/methods/invoiceOut/specs/transferinvoice.spec.js | 1 + .../ticket/back/methods/ticket/invoiceTicketsAndPdf.js | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js index 11575999a..eaaef3e26 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js @@ -17,6 +17,7 @@ describe('InvoiceOut transferInvoice()', () => { spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ active: activeCtx }); + spyOn(models.InvoiceOut, 'makePdfAndNotify'); }); it('should return the id of the created issued invoice', async() => { diff --git a/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js index 5529a3ae3..2211eb50b 100644 --- a/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js +++ b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js @@ -28,12 +28,8 @@ module.exports = function(Self) { }); Self.invoiceTicketsAndPdf = async(ctx, ticketsIds, invoiceCorrection, options) => { - let invoiceIds = null; - - invoiceIds = await Self.invoiceTickets(ctx, ticketsIds, invoiceCorrection, options); - if (!invoiceIds) return []; - if (!options.transaction) - await Self.app.models.InvoiceOut.makePdfList(ctx, invoiceIds, null, options); + const invoiceIds = await Self.invoiceTickets(ctx, ticketsIds, invoiceCorrection, options); + await Self.app.models.InvoiceOut.makePdfList(ctx, invoiceIds, null, options); return invoiceIds; }; }; From 9bf08f31ae0441ab0262c61c9c4dc372d325a269 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 29 Jan 2024 14:10:22 +0100 Subject: [PATCH 5/7] refs #6174 feat: replace method in salix-front --- modules/ticket/front/descriptor-menu/index.js | 2 +- modules/ticket/front/index/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ticket/front/descriptor-menu/index.js b/modules/ticket/front/descriptor-menu/index.js index cd819e623..297d8ee1b 100644 --- a/modules/ticket/front/descriptor-menu/index.js +++ b/modules/ticket/front/descriptor-menu/index.js @@ -265,7 +265,7 @@ class Controller extends Section { }); } - return this.$http.post(`Tickets/invoiceTickets`, {ticketsIds: [this.id]}) + return this.$http.post(`Tickets/invoiceTicketsAndPdf`, {ticketsIds: [this.id]}) .then(() => this.reload()) .then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced'))); } diff --git a/modules/ticket/front/index/index.js b/modules/ticket/front/index/index.js index b3afc838c..489f677a3 100644 --- a/modules/ticket/front/index/index.js +++ b/modules/ticket/front/index/index.js @@ -163,7 +163,7 @@ export default class Controller extends Section { makeInvoice() { const ticketsIds = this.checked.map(ticket => ticket.id); - return this.$http.post(`Tickets/invoiceTickets`, {ticketsIds}) + return this.$http.post(`Tickets/invoiceTicketsAndPdf`, {ticketsIds}) .then(() => this.$.model.refresh()) .then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced'))); } From 5396b402a53472f1a635532ad60ef791ce8e9122 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 29 Jan 2024 14:10:30 +0100 Subject: [PATCH 6/7] refs #6174 feat: acl --- db/versions/10848-blackAspidistra/00-firstScript.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 db/versions/10848-blackAspidistra/00-firstScript.sql diff --git a/db/versions/10848-blackAspidistra/00-firstScript.sql b/db/versions/10848-blackAspidistra/00-firstScript.sql new file mode 100644 index 000000000..cc9a59a24 --- /dev/null +++ b/db/versions/10848-blackAspidistra/00-firstScript.sql @@ -0,0 +1,3 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES + ('Ticket', 'makePdfList', '*', 'ALLOW', 'ROLE', 'employee'), + ('Ticket', 'invoiceTicketsAndPdf', '*', 'ALLOW', 'ROLE', 'employee'); From 2e0da0881bceb6667840f6c72ac6343eb5af4d93 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 29 Jan 2024 14:54:19 +0100 Subject: [PATCH 7/7] refs #6174 test: fix method --- modules/ticket/front/descriptor-menu/index.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js index c755b14c3..6d74881b8 100644 --- a/modules/ticket/front/descriptor-menu/index.spec.js +++ b/modules/ticket/front/descriptor-menu/index.spec.js @@ -191,7 +191,7 @@ describe('Ticket Component vnTicketDescriptorMenu', () => { jest.spyOn(controller.vnApp, 'showSuccess'); const expectedParams = {ticketsIds: [ticket.id]}; - $httpBackend.expectPOST(`Tickets/invoiceTickets`, expectedParams).respond(); + $httpBackend.expectPOST(`Tickets/invoiceTicketsAndPdf`, expectedParams).respond(); controller.makeInvoice(); $httpBackend.flush();