diff --git a/db/changes/10340-summer/00-ACL.sql b/db/changes/10340-summer/00-ACL.sql index fd92b3c1c..2824aae2a 100644 --- a/db/changes/10340-summer/00-ACL.sql +++ b/db/changes/10340-summer/00-ACL.sql @@ -3,5 +3,7 @@ DELETE FROM `salix`.`ACL` WHERE id = 188; UPDATE `salix`.`ACL` tdms SET tdms.accessType = '*' WHERE tdms.id = 165; INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) - VALUES ('InvoiceOut', 'createManualInvoice', 'WRITE', 'ALLOW', 'ROLE', 'invoicing'); + VALUES + ('InvoiceOut', 'createManualInvoice', 'WRITE', 'ALLOW', 'ROLE', 'invoicing'), + ('InvoiceOut', 'globalInvoicing', 'WRITE', 'ALLOW', 'ROLE', 'invoicing'); diff --git a/loopback/locale/en.json b/loopback/locale/en.json index d4de6e55b..1781e0975 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -110,5 +110,6 @@ "nickname": "nickname", "State": "State", "regular": "regular", - "reserved": "reserved" + "reserved": "reserved", + "Global invoicing failed": "[Global invoicing] Wasn't able to invoice some of the clients" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 92cd8d343..9c8f15a94 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -203,6 +203,6 @@ "This ticket is already invoiced": "Este ticket ya está facturado", "A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero", "A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa", - "Not invoiceable": "Not invoiceable", - "Not invoiceable 1101": "Not invoiceable 1101" + "Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes", + "Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes" } \ No newline at end of file diff --git a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js index df79a6c09..450736902 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js +++ b/modules/invoiceOut/back/methods/invoiceOut/globalInvoicing.js @@ -42,9 +42,9 @@ module.exports = Self => { }); Self.globalInvoicing = async(ctx, options) => { - const models = Self.app.models; const args = ctx.args; const invoicesIds = []; + const failedClients = []; let tx; const myOptions = {}; @@ -81,7 +81,7 @@ module.exports = Self => { const minShipped = new Date(); minShipped.setFullYear(minShipped.getFullYear() - 1); - // Liquidacion de cubos y carros + // Packaging liquidation const vIsAllInvoiceable = false; const clientsWithPackaging = await getClientsWithPackaging(ctx, myOptions); for (let client of clientsWithPackaging) { @@ -93,77 +93,70 @@ module.exports = Self => { ], myOptions); } - const company = await models.Company.findById(args.companyFk, null, myOptions); const invoiceableClients = await getInvoiceableClients(ctx, myOptions); if (!invoiceableClients.length) return; for (let client of invoiceableClients) { - // esto es para los que no tienen rol de invoicing?? - /* const [clientTax] = await Self.rawSql('SELECT vn.clientTaxArea(?, ?) AS taxArea', [ - client.id, - args.companyFk - ], myOptions); - const clientTaxArea = clientTax.taxArea; - if (clientTaxArea != 'WORLD' && company.code === 'VNL' && hasRole('invoicing')) { - // Exit process?? - console.log(clientTaxArea); - throw new UserError('Not invoiceable ' + client.id); - } - */ - if (client.hasToInvoiceByAddress) { - await Self.rawSql('CALL ticketToInvoiceByAddress(?, ?, ?, ?)', [ - minShipped, - args.maxShipped, - client.addressFk, - args.companyFk - ], myOptions); - } else { - await Self.rawSql('CALL invoiceFromClient(?, ?, ?)', [ - args.maxShipped, + 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, - args.companyFk + args.companyFk, + 'G' ], myOptions); - } + const serialLetter = invoiceSerial.serial; - // Make invoice + query = `CALL invoiceOut_new(?, ?, NULL, @invoiceId)`; + await Self.rawSql(query, [ + serialLetter, + args.invoiceDate + ], myOptions); - const isSpanishCompany = await getIsSpanishCompany(args.companyFk, myOptions); + const [newInvoice] = await Self.rawSql(`SELECT @invoiceId id`, null, myOptions); + if (newInvoice.id) { + await Self.rawSql('CALL invoiceOutBooking(?)', [newInvoice.id], myOptions); - // Validates ticket nagative base - const hasAnyNegativeBase = await getNegativeBase(myOptions); - if (hasAnyNegativeBase && isSpanishCompany) + invoicesIds.push(newInvoice.id); + } + } catch (e) { + failedClients.push({ + id: client.id, + stacktrace: e + }); 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); - - invoicesIds.push(newInvoice.id); } - - // IMPRIMIR PDF ID 3? } - // Print invoice to network printer + if (failedClients.length > 0) + await notifyFailures(ctx, failedClients, myOptions); if (tx) await tx.commit(); - // Print invoices + // Print invoices PDF for (let invoiceId of invoicesIds) await Self.createPdf(ctx, invoiceId); @@ -243,4 +236,28 @@ module.exports = Self => { args.companyFk ], options); } + + async function notifyFailures(ctx, failedClients, options) { + const models = Self.app.models; + const userId = ctx.req.accessToken.userId; + const $t = ctx.req.__; // $translate + + const worker = await models.EmailUser.findById(userId, null, options); + const subject = $t('Global invoicing failed'); + let body = $t(`Wasn't able to invoice the following clients`) + ':

'; + + for (client of failedClients) { + body += `ID: ${client.id} +
${client.stacktrace}

`; + } + + await Self.rawSql(` + INSERT INTO vn.mail (sender, replyTo, sent, subject, body) + VALUES (?, ?, FALSE, ?, ?)`, [ + worker.email, + worker.email, + subject, + body + ], options); + } }; diff --git a/modules/invoiceOut/front/index/global-invoicing/index.html b/modules/invoiceOut/front/index/global-invoicing/index.html index fd26afab9..9fd412d0e 100644 --- a/modules/invoiceOut/front/index/global-invoicing/index.html +++ b/modules/invoiceOut/front/index/global-invoicing/index.html @@ -14,6 +14,14 @@ data="companies" order="code"> +
+ + + Invoicing in progress... + +
super.responseHandler(response)) - .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) + .finally(() => this.isInvoicing = false); } catch (e) { this.vnApp.showError(this.$t(e.message)); + this.isInvoicing = false; return false; } } diff --git a/modules/invoiceOut/front/index/global-invoicing/locale/es.yml b/modules/invoiceOut/front/index/global-invoicing/locale/es.yml index b4b13945c..51e165e2a 100644 --- a/modules/invoiceOut/front/index/global-invoicing/locale/es.yml +++ b/modules/invoiceOut/front/index/global-invoicing/locale/es.yml @@ -1,3 +1,9 @@ Create global invoice: Crear factura global Some fields are required: Algunos campos son obligatorios -Max date: Fecha límite \ No newline at end of file +Max date: Fecha límite +Invoicing in progress...: Facturación en progreso... +Invoice date: Fecha de factura +From client: Desde el cliente +To client: Hasta el cliente +Invoice date and the max date should be filled: La fecha de factura y la fecha límite deben rellenarse +Choose a valid clients range: Selecciona un rango válido de clientes \ No newline at end of file diff --git a/modules/invoiceOut/front/index/global-invoicing/style.scss b/modules/invoiceOut/front/index/global-invoicing/style.scss index 4e2435c21..d0bd9e214 100644 --- a/modules/invoiceOut/front/index/global-invoicing/style.scss +++ b/modules/invoiceOut/front/index/global-invoicing/style.scss @@ -1,5 +1,17 @@ +@import "variables"; + .vn-invoice-out-global-invoicing { tpl-body { - width: 500px + width: 500px; + + .progress { + font-weight: bold; + text-align: center; + font-size: 1.5rem; + color: $color-primary; + vn-horizontal { + justify-content: center + } + } } } \ No newline at end of file diff --git a/modules/invoiceOut/front/index/manual/index.html b/modules/invoiceOut/front/index/manual/index.html index f2fd10d6f..3148d3f94 100644 --- a/modules/invoiceOut/front/index/manual/index.html +++ b/modules/invoiceOut/front/index/manual/index.html @@ -14,6 +14,14 @@ data="taxAreas" order="code"> +
+ + + Invoicing in progress... + +
{ this.$state.go('invoiceOut.card.summary', {id: res.data.id}); super.responseHandler(response); }) - .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) + .finally(() => this.isInvoicing = false); } catch (e) { this.vnApp.showError(this.$t(e.message)); + this.isInvoicing = false; return false; } } diff --git a/modules/invoiceOut/front/index/manual/locale/es.yml b/modules/invoiceOut/front/index/manual/locale/es.yml index 826057c8d..370e823d0 100644 --- a/modules/invoiceOut/front/index/manual/locale/es.yml +++ b/modules/invoiceOut/front/index/manual/locale/es.yml @@ -2,4 +2,5 @@ Create manual invoice: Crear factura manual Some fields are required: Algunos campos son obligatorios Client and max shipped fields should be filled: Los campos de cliente y fecha límite deben rellenarse Max date: Fecha límite -Serial: Serie \ No newline at end of file +Serial: Serie +Invoicing in progress...: Facturación en progreso... \ No newline at end of file diff --git a/modules/invoiceOut/front/index/manual/style.scss b/modules/invoiceOut/front/index/manual/style.scss index 998472157..18e6f3513 100644 --- a/modules/invoiceOut/front/index/manual/style.scss +++ b/modules/invoiceOut/front/index/manual/style.scss @@ -1,5 +1,17 @@ +@import "variables"; + .vn-invoice-out-manual { tpl-body { - width: 500px + width: 500px; + + .progress { + font-weight: bold; + text-align: center; + font-size: 1.5rem; + color: $color-primary; + vn-horizontal { + justify-content: center + } + } } } \ No newline at end of file