Global invoicing improvements
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
parent
3a45c99594
commit
e65e90d00f
|
@ -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');
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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`) + ':<br/><br/>';
|
||||
|
||||
for (client of failedClients) {
|
||||
body += `ID: <strong>${client.id}</strong>
|
||||
<br/> <strong>${client.stacktrace}</strong><br/><br/>`;
|
||||
}
|
||||
|
||||
await Self.rawSql(`
|
||||
INSERT INTO vn.mail (sender, replyTo, sent, subject, body)
|
||||
VALUES (?, ?, FALSE, ?, ?)`, [
|
||||
worker.email,
|
||||
worker.email,
|
||||
subject,
|
||||
body
|
||||
], options);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
data="companies"
|
||||
order="code">
|
||||
</vn-crud-model>
|
||||
<div
|
||||
class="progress vn-my-md"
|
||||
ng-if="$ctrl.isInvoicing">
|
||||
<vn-horizontal>
|
||||
<vn-icon vn-none icon="warning"></vn-icon>
|
||||
<span vn-none translate>Invoicing in progress...</span>
|
||||
</vn-horizontal>
|
||||
</div>
|
||||
<vn-horizontal>
|
||||
<vn-date-picker
|
||||
vn-one
|
||||
|
|
|
@ -6,6 +6,7 @@ class Controller extends Dialog {
|
|||
constructor($element, $, $transclude) {
|
||||
super($element, $, $transclude);
|
||||
|
||||
this.isInvoicing = false;
|
||||
this.invoice = {
|
||||
maxShipped: new Date()
|
||||
};
|
||||
|
@ -50,17 +51,20 @@ class Controller extends Dialog {
|
|||
if (response !== 'accept')
|
||||
return super.responseHandler(response);
|
||||
|
||||
/* if (this.invoice.clientFk && !this.invoice.maxShipped)
|
||||
throw new Error('Client and the max shipped should be filled');
|
||||
if (!this.invoice.invoiceDate || !this.invoice.maxShipped)
|
||||
throw new Error('Invoice date and the max date should be filled');
|
||||
|
||||
if (!this.invoice.serial || !this.invoice.taxArea)
|
||||
throw new Error('Some fields are required'); */
|
||||
if (!this.invoice.fromClientId || !this.invoice.toClientId)
|
||||
throw new Error('Choose a valid clients range');
|
||||
|
||||
this.isInvoicing = true;
|
||||
return this.$http.post(`InvoiceOuts/globalInvoicing`, this.invoice)
|
||||
.then(() => 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
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...
|
||||
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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,14 @@
|
|||
data="taxAreas"
|
||||
order="code">
|
||||
</vn-crud-model>
|
||||
<div
|
||||
class="progress vn-my-md"
|
||||
ng-if="$ctrl.isInvoicing">
|
||||
<vn-horizontal>
|
||||
<vn-icon vn-none icon="warning"></vn-icon>
|
||||
<span vn-none translate>Invoicing in progress...</span>
|
||||
</vn-horizontal>
|
||||
</div>
|
||||
<vn-horizontal class="manifold-panel">
|
||||
<vn-autocomplete
|
||||
url="Tickets"
|
||||
|
|
|
@ -6,6 +6,7 @@ class Controller extends Dialog {
|
|||
constructor($element, $, $transclude) {
|
||||
super($element, $, $transclude);
|
||||
|
||||
this.isInvoicing = false;
|
||||
this.invoice = {
|
||||
maxShipped: new Date()
|
||||
};
|
||||
|
@ -22,14 +23,17 @@ class Controller extends Dialog {
|
|||
if (!this.invoice.serial || !this.invoice.taxArea)
|
||||
throw new Error('Some fields are required');
|
||||
|
||||
this.isInvoicing = true;
|
||||
return this.$http.post(`InvoiceOuts/createManualInvoice`, this.invoice)
|
||||
.then(res => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,3 +3,4 @@ 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
|
||||
Invoicing in progress...: Facturación en progreso...
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue