2734 - Make invoice PDF refactor
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
1ed4dd27d6
commit
bd081f9fd6
|
@ -48,7 +48,7 @@ module.exports = Self => {
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
if (process.env.NODE_ENV == 'test')
|
if (process.env.NODE_ENV == 'test')
|
||||||
throw new UserError(`You can't upload images on the test environment`);
|
throw new UserError(`Action not allowed on the test environment`);
|
||||||
|
|
||||||
// Upload file to temporary path
|
// Upload file to temporary path
|
||||||
const tempContainer = await TempContainer.container(args.collection);
|
const tempContainer = await TempContainer.container(args.collection);
|
||||||
|
|
|
@ -164,7 +164,7 @@
|
||||||
"Amount cannot be zero": "El importe no puede ser cero",
|
"Amount cannot be zero": "El importe no puede ser cero",
|
||||||
"Company has to be official": "Empresa inválida",
|
"Company has to be official": "Empresa inválida",
|
||||||
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
|
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
|
||||||
"You can't upload images on the test environment": "No puedes subir imágenes en el entorno de pruebas",
|
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
|
||||||
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
|
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
|
||||||
"Sorts whole route": "Reordena ruta entera",
|
"Sorts whole route": "Reordena ruta entera",
|
||||||
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día <strong>{{shipped}}</strong>, con una cantidad de <strong>{{quantity}}</strong> y un precio de <strong>{{price}} €</strong>",
|
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día <strong>{{shipped}}</strong>, con una cantidad de <strong>{{quantity}}</strong> y un precio de <strong>{{price}} €</strong>",
|
||||||
|
|
|
@ -199,7 +199,7 @@
|
||||||
<span
|
<span
|
||||||
ng-click="ticketDescriptor.show($event, action.sale.ticket.id)"
|
ng-click="ticketDescriptor.show($event, action.sale.ticket.id)"
|
||||||
class="link">
|
class="link">
|
||||||
{{::action.sale.ticket.id | zeroFill:6}}
|
{{::action.sale.ticket.id}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td expand>{{::action.claimBeggining.description}}</vn-td>
|
<vn-td expand>{{::action.claimBeggining.description}}</vn-td>
|
||||||
|
|
|
@ -21,10 +21,20 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.book = async ref => {
|
Self.book = async ref => {
|
||||||
let ticketAddress = await Self.app.models.Ticket.findOne({where: {invoiceOut: ref}});
|
const models = Self.app.models;
|
||||||
let invoiceCompany = await Self.app.models.InvoiceOut.findOne({where: {ref: ref}});
|
const ticketAddress = await models.Ticket.findOne({
|
||||||
let [taxArea] = await Self.rawSql(`Select vn.addressTaxArea(?, ?) AS code`, [ticketAddress.address, invoiceCompany.company]);
|
where: {invoiceOut: ref}
|
||||||
|
});
|
||||||
|
const invoiceCompany = await models.InvoiceOut.findOne({
|
||||||
|
where: {ref: ref}
|
||||||
|
});
|
||||||
|
let query = 'SELECT vn.addressTaxArea(?, ?) AS code';
|
||||||
|
const [taxArea] = await Self.rawSql(query, [
|
||||||
|
ticketAddress.address,
|
||||||
|
invoiceCompany.company
|
||||||
|
]);
|
||||||
|
|
||||||
return Self.rawSql(`CALL vn.invoiceOutAgain(?, ?)`, [ref, taxArea.code]);
|
query = 'CALL vn.invoiceOutAgain(?, ?)';
|
||||||
|
return Self.rawSql(query, [ref, taxArea.code]);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,37 +5,26 @@ const path = require('path');
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('createPdf', {
|
Self.remoteMethodCtx('createPdf', {
|
||||||
description: 'Creates an invoice PDF',
|
description: 'Creates an invoice PDF',
|
||||||
accessType: 'READ',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'String',
|
type: 'number',
|
||||||
description: 'The invoice id',
|
description: 'The invoice id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: [
|
returns: {
|
||||||
{
|
type: 'object',
|
||||||
arg: 'body',
|
|
||||||
type: 'file',
|
|
||||||
root: true
|
root: true
|
||||||
}, {
|
},
|
||||||
arg: 'Content-Type',
|
|
||||||
type: 'String',
|
|
||||||
http: {target: 'header'}
|
|
||||||
}, {
|
|
||||||
arg: 'Content-Disposition',
|
|
||||||
type: 'String',
|
|
||||||
http: {target: 'header'}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
http: {
|
http: {
|
||||||
path: `/:id/createPdf`,
|
path: `/:id/createPdf`,
|
||||||
verb: 'GET'
|
verb: 'POST'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.createPdf = async function(ctx, id, options = {}) {
|
Self.createPdf = async function(ctx, id, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const headers = ctx.req.headers;
|
const headers = ctx.req.headers;
|
||||||
const origin = headers.origin;
|
const origin = headers.origin;
|
||||||
|
@ -44,10 +33,23 @@ module.exports = Self => {
|
||||||
if (process.env.NODE_ENV == 'test')
|
if (process.env.NODE_ENV == 'test')
|
||||||
throw new UserError(`Action not allowed on the test environment`);
|
throw new UserError(`Action not allowed on the test environment`);
|
||||||
|
|
||||||
const invoiceOut = await Self.findById(id);
|
let tx;
|
||||||
|
let newOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(newOptions, options);
|
||||||
|
|
||||||
|
if (!newOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
newOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileSrc;
|
||||||
|
try {
|
||||||
|
const invoiceOut = await Self.findById(id, null, newOptions);
|
||||||
await invoiceOut.updateAttributes({
|
await invoiceOut.updateAttributes({
|
||||||
hasPdf: true
|
hasPdf: true
|
||||||
});
|
}, newOptions);
|
||||||
|
|
||||||
const response = got.stream(`${origin}/api/report/invoice`, {
|
const response = got.stream(`${origin}/api/report/invoice`, {
|
||||||
query: {
|
query: {
|
||||||
|
@ -60,7 +62,7 @@ module.exports = Self => {
|
||||||
const container = await models.InvoiceContainer.container(invoiceYear);
|
const container = await models.InvoiceContainer.container(invoiceYear);
|
||||||
const rootPath = container.client.root;
|
const rootPath = container.client.root;
|
||||||
const fileName = `${invoiceOut.ref}.pdf`;
|
const fileName = `${invoiceOut.ref}.pdf`;
|
||||||
const fileSrc = path.join(rootPath, invoiceYear, fileName);
|
fileSrc = path.join(rootPath, invoiceYear, fileName);
|
||||||
|
|
||||||
const writeStream = fs.createWriteStream(fileSrc);
|
const writeStream = fs.createWriteStream(fileSrc);
|
||||||
writeStream.on('open', () => {
|
writeStream.on('open', () => {
|
||||||
|
@ -70,5 +72,15 @@ module.exports = Self => {
|
||||||
writeStream.on('finish', async function() {
|
writeStream.on('finish', async function() {
|
||||||
writeStream.end();
|
writeStream.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return invoiceOut;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
if (fs.existsSync(fileSrc))
|
||||||
|
await fs.unlink(fileSrc);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('regenerate', {
|
|
||||||
description: 'Sends an invoice to a regeneration queue',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'id',
|
|
||||||
type: 'number',
|
|
||||||
required: true,
|
|
||||||
description: 'The invoiceOut id',
|
|
||||||
http: {source: 'path'}
|
|
||||||
}],
|
|
||||||
returns: {
|
|
||||||
type: 'object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: '/:id/regenerate',
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.regenerate = async(ctx, id) => {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const tx = await Self.beginTransaction({});
|
|
||||||
|
|
||||||
try {
|
|
||||||
let options = {transaction: tx};
|
|
||||||
|
|
||||||
// Remove all invoice references from tickets
|
|
||||||
const invoiceOut = await models.InvoiceOut.findById(id, null, options);
|
|
||||||
await invoiceOut.updateAttributes({
|
|
||||||
hasPdf: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create invoice PDF
|
|
||||||
await models.InvoiceOut.createPdf(ctx, id);
|
|
||||||
|
|
||||||
await tx.commit();
|
|
||||||
|
|
||||||
return invoiceOut;
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,36 +0,0 @@
|
||||||
const app = require('vn-loopback/server/server');
|
|
||||||
|
|
||||||
describe('invoiceOut regenerate()', () => {
|
|
||||||
const invoiceReportFk = 30;
|
|
||||||
const invoiceOutId = 1;
|
|
||||||
|
|
||||||
it('should check that the invoice has a PDF and is not in print generation queue', async() => {
|
|
||||||
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
|
|
||||||
const [queue] = await app.models.InvoiceOut.rawSql(`
|
|
||||||
SELECT COUNT(*) AS total
|
|
||||||
FROM vn.printServerQueue
|
|
||||||
WHERE reportFk = ?`, [invoiceReportFk]);
|
|
||||||
|
|
||||||
expect(invoiceOut.hasPdf).toBeTruthy();
|
|
||||||
expect(queue.total).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should mark the invoice as doesn't have PDF and add it to a print queue`, async() => {
|
|
||||||
const ctx = {req: {accessToken: {userId: 5}}};
|
|
||||||
const invoiceOut = await app.models.InvoiceOut.regenerate(ctx, invoiceOutId);
|
|
||||||
const [queue] = await app.models.InvoiceOut.rawSql(`
|
|
||||||
SELECT COUNT(*) AS total
|
|
||||||
FROM vn.printServerQueue
|
|
||||||
WHERE reportFk = ?`, [invoiceReportFk]);
|
|
||||||
|
|
||||||
expect(invoiceOut.hasPdf).toBeFalsy();
|
|
||||||
expect(queue.total).toEqual(1);
|
|
||||||
|
|
||||||
// restores
|
|
||||||
const invoiceOutToRestore = await app.models.InvoiceOut.findById(invoiceOutId);
|
|
||||||
await invoiceOutToRestore.updateAttributes({hasPdf: true});
|
|
||||||
await app.models.InvoiceOut.rawSql(`
|
|
||||||
DELETE FROM vn.printServerQueue
|
|
||||||
WHERE reportFk = ?`, [invoiceReportFk]);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -2,7 +2,6 @@ module.exports = Self => {
|
||||||
require('../methods/invoiceOut/filter')(Self);
|
require('../methods/invoiceOut/filter')(Self);
|
||||||
require('../methods/invoiceOut/summary')(Self);
|
require('../methods/invoiceOut/summary')(Self);
|
||||||
require('../methods/invoiceOut/download')(Self);
|
require('../methods/invoiceOut/download')(Self);
|
||||||
require('../methods/invoiceOut/regenerate')(Self);
|
|
||||||
require('../methods/invoiceOut/delete')(Self);
|
require('../methods/invoiceOut/delete')(Self);
|
||||||
require('../methods/invoiceOut/book')(Self);
|
require('../methods/invoiceOut/book')(Self);
|
||||||
require('../methods/invoiceOut/createPdf')(Self);
|
require('../methods/invoiceOut/createPdf')(Self);
|
||||||
|
|
|
@ -25,6 +25,14 @@
|
||||||
translate>
|
translate>
|
||||||
Book invoice
|
Book invoice
|
||||||
</vn-item>
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="createInvoicePdfConfirmation.show()"
|
||||||
|
vn-acl="invoicing"
|
||||||
|
vn-acl-action="remove"
|
||||||
|
name="regenerateInvoice"
|
||||||
|
translate>
|
||||||
|
Regenerate invoice PDF
|
||||||
|
</vn-item>
|
||||||
</slot-menu>
|
</slot-menu>
|
||||||
<slot-body>
|
<slot-body>
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
|
@ -82,3 +90,11 @@
|
||||||
<vn-client-descriptor-popover
|
<vn-client-descriptor-popover
|
||||||
vn-id="clientDescriptor">
|
vn-id="clientDescriptor">
|
||||||
</vn-client-descriptor-popover>
|
</vn-client-descriptor-popover>
|
||||||
|
|
||||||
|
<!-- Create invoice PDF confirmation dialog -->
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="createInvoicePdfConfirmation"
|
||||||
|
on-accept="$ctrl.createInvoicePdf()"
|
||||||
|
question="Are you sure you want to regenerate the invoice PDF document?"
|
||||||
|
message="You are going to regenerate the invoice PDF document">
|
||||||
|
</vn-confirm>
|
|
@ -22,6 +22,16 @@ class Controller extends Descriptor {
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
|
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInvoicePdf() {
|
||||||
|
const invoiceId = this.invoiceOut.id;
|
||||||
|
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
||||||
|
.then(() => {
|
||||||
|
const snackbarMessage = this.$t(
|
||||||
|
`The invoice PDF document has been regenerated`);
|
||||||
|
this.vnApp.showSuccess(snackbarMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
get filter() {
|
get filter() {
|
||||||
if (this.invoiceOut)
|
if (this.invoiceOut)
|
||||||
return JSON.stringify({refFk: this.invoiceOut.ref});
|
return JSON.stringify({refFk: this.invoiceOut.ref});
|
||||||
|
|
|
@ -9,3 +9,5 @@ Are you sure you want to delete this invoice?: Estas seguro de eliminar esta fac
|
||||||
Book invoice: Asentar factura
|
Book invoice: Asentar factura
|
||||||
InvoiceOut booked: Factura asentada
|
InvoiceOut booked: Factura asentada
|
||||||
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||||
|
Regenerate invoice PDF: Regenerar PDF factura
|
||||||
|
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
|
@ -67,7 +67,7 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
if (serial != 'R' && invoiceId) {
|
if (serial != 'R' && invoiceId) {
|
||||||
await Self.rawSql('CALL invoiceOutBooking(?)', [invoiceId], options);
|
await Self.rawSql('CALL invoiceOutBooking(?)', [invoiceId], options);
|
||||||
await models.InvoiceOut.createPdf(ctx, invoiceId);
|
await models.InvoiceOut.createPdf(ctx, invoiceId, options);
|
||||||
}
|
}
|
||||||
await tx.commit();
|
await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -80,13 +80,13 @@
|
||||||
Make invoice
|
Make invoice
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="regenerateInvoiceConfirmation.show()"
|
ng-click="createInvoicePdfConfirmation.show()"
|
||||||
ng-show="$ctrl.isInvoiced"
|
ng-show="$ctrl.isInvoiced"
|
||||||
vn-acl="invoicing"
|
vn-acl="invoicing"
|
||||||
vn-acl-action="remove"
|
vn-acl-action="remove"
|
||||||
name="regenerateInvoice"
|
name="regenerateInvoice"
|
||||||
translate>
|
translate>
|
||||||
Regenerate invoice
|
Regenerate invoice PDF
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="recalculateComponentsConfirmation.show()"
|
ng-click="recalculateComponentsConfirmation.show()"
|
||||||
|
@ -207,12 +207,12 @@
|
||||||
message="Are you sure you want to invoice this ticket?">
|
message="Are you sure you want to invoice this ticket?">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
<!-- Regenerate invoice confirmation dialog -->
|
<!-- Create invoice PDF confirmation dialog -->
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
vn-id="regenerateInvoiceConfirmation"
|
vn-id="createInvoicePdfConfirmation"
|
||||||
on-accept="$ctrl.regenerateInvoice()"
|
on-accept="$ctrl.createInvoicePdf()"
|
||||||
question="You are going to regenerate the invoice"
|
question="Are you sure you want to regenerate the invoice PDF document?"
|
||||||
message="Are you sure you want to regenerate the invoice?">
|
message="You are going to regenerate the invoice PDF document">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
<!-- Recalculate components confirmation dialog -->
|
<!-- Recalculate components confirmation dialog -->
|
||||||
|
|
|
@ -219,12 +219,12 @@ class Controller extends Section {
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced')));
|
.then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced')));
|
||||||
}
|
}
|
||||||
|
|
||||||
regenerateInvoice() {
|
createInvoicePdf() {
|
||||||
const invoiceId = this.ticket.invoiceOut.id;
|
const invoiceId = this.ticket.invoiceOut.id;
|
||||||
return this.$http.post(`InvoiceOuts/${invoiceId}/regenerate`)
|
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const snackbarMessage = this.$t(
|
const snackbarMessage = this.$t(
|
||||||
`Invoice sent for a regeneration, will be available in a few minutes`);
|
`The invoice PDF document has been regenerated`);
|
||||||
this.vnApp.showSuccess(snackbarMessage);
|
this.vnApp.showSuccess(snackbarMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,12 @@ Make a payment: "Verdnatura le comunica:\rSu pedido está pendiente de pago.\rPo
|
||||||
Minimum is needed: "Verdnatura le recuerda:\rEs necesario un importe mínimo de 50€ (Sin IVA) en su pedido {{ticketId}} del día {{created | date: 'dd/MM/yyyy'}} para recibirlo sin portes adicionales."
|
Minimum is needed: "Verdnatura le recuerda:\rEs necesario un importe mínimo de 50€ (Sin IVA) en su pedido {{ticketId}} del día {{created | date: 'dd/MM/yyyy'}} para recibirlo sin portes adicionales."
|
||||||
Ticket invoiced: Ticket facturado
|
Ticket invoiced: Ticket facturado
|
||||||
Make invoice: Crear factura
|
Make invoice: Crear factura
|
||||||
Regenerate invoice: Regenerar factura
|
Regenerate invoice PDF: Regenerar PDF factura
|
||||||
|
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
||||||
You are going to invoice this ticket: Vas a facturar este ticket
|
You are going to invoice this ticket: Vas a facturar este ticket
|
||||||
Are you sure you want to invoice this ticket?: ¿Seguro que quieres facturar este ticket?
|
Are you sure you want to invoice this ticket?: ¿Seguro que quieres facturar este ticket?
|
||||||
You are going to regenerate the invoice: Vas a regenerar la factura
|
You are going to regenerate the invoice PDF document: Vas a regenerar el documento PDF de la factura
|
||||||
Are you sure you want to regenerate the invoice?: ¿Seguro que quieres regenerar la factura?
|
Are you sure you want to regenerate the invoice PDF document?: ¿Seguro que quieres regenerar el documento PDF de la factura?
|
||||||
Invoice sent for a regeneration, will be available in a few minutes: La factura ha sido enviada para ser regenerada, estará disponible en unos minutos
|
|
||||||
Shipped hour updated: Hora de envio modificada
|
Shipped hour updated: Hora de envio modificada
|
||||||
Deleted ticket: Ticket eliminado
|
Deleted ticket: Ticket eliminado
|
||||||
Recalculate components: Recalcular componentes
|
Recalculate components: Recalcular componentes
|
||||||
|
|
Loading…
Reference in New Issue