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'); diff --git a/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js b/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js new file mode 100644 index 000000000..a0449aeda --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/makePdfList.js @@ -0,0 +1,31 @@ + +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) { + const pdfs = ids.map(id => + Self.makePdfAndNotify(ctx, id, printerFk, options) + ); + await Promise.all(pdfs); + }; +}; 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/invoiceOut/back/methods/invoiceOut/transferInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js index 782eaf6d8..8e234d7cc 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js @@ -86,17 +86,17 @@ 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); - if (tx) { - await tx.commit(); - await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null); - } + const [invoiceId] = await models.Ticket.invoiceTicketsAndPdf(ctx, clonedTicketIds, null, myOptions); return invoiceId; } catch (e) { 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 06429836e..53400e724 100644 --- a/modules/ticket/back/methods/ticket/invoiceTickets.js +++ b/modules/ticket/back/methods/ticket/invoiceTickets.js @@ -76,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 (tx) { - 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..2211eb50b --- /dev/null +++ b/modules/ticket/back/methods/ticket/invoiceTicketsAndPdf.js @@ -0,0 +1,36 @@ + +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) => { + const invoiceIds = await Self.invoiceTickets(ctx, ticketsIds, invoiceCorrection, options); + await Self.app.models.InvoiceOut.makePdfList(ctx, invoiceIds, null, options); + return invoiceIds; + }; +}; + 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/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; + } + }); +}); 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); }; 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/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(); 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'))); }