3092-module_transactions #740

Merged
joan merged 41 commits from 3092-module_transactions into dev 2021-10-18 07:42:24 +00:00
25 changed files with 142 additions and 78 deletions
Showing only changes of commit 16b27c2074 - Show all commits

View File

@ -23,5 +23,13 @@
"model": "Account", "model": "Account",
"foreignKey": "userFk" "foreignKey": "userFk"
} }
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
} }
]
} }

View File

@ -0,0 +1 @@
UPDATE salix.ACL t SET t.principalId = 'employee' WHERE t.id = 269;

View File

@ -489,11 +489,11 @@ INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaF
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`) INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
VALUES VALUES
(1, 'T', 1014.24, CURDATE(), 1101, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), (1, 'T', 1014.24, CURDATE(), 1101, CURDATE(), 442, CURDATE(), CURDATE(), 1, 0),
(2, 'T', 121.36, CURDATE(), 1102, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), (2, 'T', 121.36, CURDATE(), 1102, CURDATE(), 442, CURDATE(), CURDATE(), 1, 0),
(3, 'T', 8.88, CURDATE(), 1103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), (3, 'T', 8.88, CURDATE(), 1103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 0),
(4, 'T', 8.88, CURDATE(), 1103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 1), (4, 'T', 8.88, CURDATE(), 1103, CURDATE(), 442, CURDATE(), CURDATE(), 1, 0),
(5, 'A', 8.88, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1103, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 442, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1); (5, 'A', 8.88, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1103, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 442, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 0);
UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1; UPDATE `vn`.`invoiceOut` SET ref = 'T1111111' WHERE id = 1;
UPDATE `vn`.`invoiceOut` SET ref = 'T2222222' WHERE id = 2; UPDATE `vn`.`invoiceOut` SET ref = 'T2222222' WHERE id = 2;

View File

@ -115,5 +115,6 @@
"A ticket with a negative base can't be invoiced": "A ticket with a negative base can't be invoiced", "A ticket with a negative base can't be invoiced": "A ticket with a negative base can't be invoiced",
"This client is not invoiceable": "This client is not invoiceable", "This client is not invoiceable": "This client is not invoiceable",
"INACTIVE_PROVIDER": "Inactive provider", "INACTIVE_PROVIDER": "Inactive provider",
"reference duplicated": "reference duplicated" "reference duplicated": "reference duplicated",
"The PDF document does not exists": "The PDF document does not exists. Try regenerating it from 'Regenerate invoice PDF' option"
} }

View File

@ -209,5 +209,6 @@
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes", "Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio", "Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito", "You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente" "You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente",
"The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'"
} }

View File

@ -19,7 +19,7 @@ module.exports = Self => {
} }
}); });
Self.hasCustomerRole = (id, options) => { Self.hasCustomerRole = async(id, options) => {
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
@ -31,7 +31,9 @@ module.exports = Self => {
JOIN salix.Role r ON r.id = A.roleFK JOIN salix.Role r ON r.id = A.roleFK
WHERE r.name = 'customer' WHERE r.name = 'customer'
AND A.id IN (?)`; AND A.id IN (?)`;
const [result] = await Self.rawSql(query, [id], myOptions);
const {isCustomer} = result;
return Self.rawSql(query, [id], myOptions); return isCustomer;
}; };
}; };

View File

@ -9,9 +9,9 @@ describe('Client hasCustomerRole', () => {
const id = 1101; const id = 1101;
const [result] = await models.Client.hasCustomerRole(id, options); const result = await models.Client.hasCustomerRole(id, options);
expect(result).toEqual(jasmine.objectContaining({isCustomer: 1})); expect(result).toBeTruthy();
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -27,9 +27,9 @@ describe('Client hasCustomerRole', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const id = 8; const id = 8;
const [result] = await models.Client.hasCustomerRole(id, options); const result = await models.Client.hasCustomerRole(id, options);
expect(result).toEqual(jasmine.objectContaining({isCustomer: 0})); expect(result).toBeFalsy();
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -46,9 +46,9 @@ describe('Client hasCustomerRole', () => {
const id = 999; const id = 999;
const [result] = await models.Client.hasCustomerRole(id, options); const result = await models.Client.hasCustomerRole(id, options);
expect(result).toEqual(jasmine.objectContaining({isCustomer: 0})); expect(result).toBeFalsy();
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -65,9 +65,9 @@ describe('Client hasCustomerRole', () => {
const id = 'WRONG!'; const id = 'WRONG!';
const [result] = await models.Client.hasCustomerRole(id, options); const result = await models.Client.hasCustomerRole(id, options);
expect(result).toEqual(jasmine.objectContaining({isCustomer: 0})); expect(result).toBeFalsy();
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -19,7 +19,7 @@ export default class Controller extends Section {
isCustomer() { isCustomer() {
if (this.client.id) { if (this.client.id) {
this.$http.get(`Clients/${this.client.id}/hasCustomerRole`).then(res => { this.$http.get(`Clients/${this.client.id}/hasCustomerRole`).then(res => {
this.canChangePassword = res.data && res.data.isCustomer; this.canChangePassword = res.data && res.data;
}); });
} }
} }

View File

@ -33,7 +33,7 @@ describe('Component VnClientWebAccess', () => {
it('should return true if the password can be modified', () => { it('should return true if the password can be modified', () => {
controller.client = {id: '1234'}; controller.client = {id: '1234'};
$httpBackend.expectGET(`Clients/${controller.client.id}/hasCustomerRole`).respond({isCustomer: true}); $httpBackend.expectGET(`Clients/${controller.client.id}/hasCustomerRole`).respond(true);
controller.isCustomer(); controller.isCustomer();
$httpBackend.flush(); $httpBackend.flush();
@ -43,7 +43,7 @@ describe('Component VnClientWebAccess', () => {
it(`should return a false if the password can't be modified`, () => { it(`should return a false if the password can't be modified`, () => {
controller.client = {id: '1234'}; controller.client = {id: '1234'};
$httpBackend.expectGET(`Clients/${controller.client.id}/hasCustomerRole`).respond({isCustomer: false}); $httpBackend.expectGET(`Clients/${controller.client.id}/hasCustomerRole`).respond(false);
controller.isCustomer(); controller.isCustomer();
$httpBackend.flush(); $httpBackend.flush();

View File

@ -29,7 +29,7 @@ module.exports = Self => {
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;
const authorization = headers.authorization; const auth = ctx.req.accessToken;
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`);
@ -48,25 +48,30 @@ module.exports = Self => {
let fileSrc; let fileSrc;
try { try {
const invoiceOut = await Self.findById(id, null, myOptions); const invoiceOut = await Self.findById(id, null, myOptions);
const hasInvoicing = await models.Account.hasRole(auth.userId, 'invoicing', myOptions);
if (invoiceOut.hasPdf && !hasInvoicing)
throw new UserError(`You don't have enough privileges`);
await invoiceOut.updateAttributes({ await invoiceOut.updateAttributes({
hasPdf: true hasPdf: true
}, myOptions); }, myOptions);
const response = got.stream(`${origin}/api/report/invoice`, { const response = got.stream(`${origin}/api/report/invoice`, {
query: { query: {
authorization: authorization, authorization: auth.id,
invoiceId: id invoiceId: id
} }
}); });
const created = invoiceOut.created; const issued = invoiceOut.issued;
const year = created.getFullYear().toString(); const year = issued.getFullYear().toString();
const month = created.getMonth().toString(); const month = (issued.getMonth() + 1).toString();
const day = created.getDate().toString(); const day = issued.getDate().toString();
const container = await models.InvoiceContainer.container(year); const container = await models.InvoiceContainer.container(year);
const rootPath = container.client.root; const rootPath = container.client.root;
const fileName = `${invoiceOut.ref}.pdf`; const fileName = `${year}${invoiceOut.ref}.pdf`;
const src = path.join(rootPath, year, month, day); const src = path.join(rootPath, year, month, day);
fileSrc = path.join(src, fileName); fileSrc = path.join(src, fileName);
@ -75,16 +80,11 @@ module.exports = Self => {
if (tx) await tx.commit(); if (tx) await tx.commit();
const writeStream = fs.createWriteStream(fileSrc); const writeStream = fs.createWriteStream(fileSrc);
writeStream.on('open', () => { writeStream.on('open', () => response.pipe(writeStream));
response.pipe(writeStream); writeStream.on('finish', () => writeStream.end());
});
return new Promise(resolve => { return new Promise(resolve => {
writeStream.on('finish', () => { writeStream.on('close', () => resolve(invoiceOut));
writeStream.end();
resolve(invoiceOut);
});
}); });
} catch (e) { } catch (e) {
if (tx) await tx.rollback(); if (tx) await tx.rollback();

View File

@ -1,8 +1,9 @@
const fs = require('fs-extra'); const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('download', { Self.remoteMethodCtx('download', {
description: 'Download an invoice PDF', description: 'Download an invoice PDF',
accessType: 'READ', accessType: 'READ',
accepts: [ accepts: [
@ -34,34 +35,41 @@ module.exports = Self => {
} }
}); });
Self.download = async function(id, options) { Self.download = async function(ctx, id, options) {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
try {
const invoiceOut = await models.InvoiceOut.findById(id, null, myOptions); const invoiceOut = await models.InvoiceOut.findById(id, null, myOptions);
const created = invoiceOut.created; const issued = invoiceOut.issued;
const year = created.getFullYear().toString(); const year = issued.getFullYear().toString();
const month = created.getMonth().toString(); const month = (issued.getMonth() + 1).toString();
const day = created.getDate().toString(); const day = issued.getDate().toString();
const container = await models.InvoiceContainer.container(year); const container = await models.InvoiceContainer.container(year);
const rootPath = container.client.root; const rootPath = container.client.root;
const src = path.join(rootPath, year, month, day); const src = path.join(rootPath, year, month, day);
const fileName = `${invoiceOut.ref}.pdf`; const fileName = `${year}${invoiceOut.ref}.pdf`;
const fileSrc = path.join(src, fileName); const fileSrc = path.join(src, fileName);
const file = { const file = {
path: fileSrc, path: fileSrc,
contentType: 'application/pdf', contentType: 'application/pdf',
name: `${id}.pdf` name: fileName
}; };
await fs.access(file.path); await fs.access(file.path);
let stream = fs.createReadStream(file.path); let stream = fs.createReadStream(file.path);
return [stream, file.contentType, `filename="${file.name}"`]; return [stream, file.contentType, `filename="${file.name}"`];
} catch (error) {
if (error.code === 'ENOENT')
throw new UserError('The PDF document does not exists');
throw error;
}
}; };
}; };

View File

@ -2,16 +2,27 @@ const models = require('vn-loopback/server/server').models;
const fs = require('fs-extra'); const fs = require('fs-extra');
describe('InvoiceOut download()', () => { describe('InvoiceOut download()', () => {
it('should return the downloaded fine name', async() => { const userId = 9;
const invoiceId = 1;
const ctx = {
req: {
accessToken: {userId: userId},
headers: {origin: 'http://localhost:5000'},
}
};
it('should return the downloaded file name', async() => {
spyOn(models.InvoiceContainer, 'container').and.returnValue({ spyOn(models.InvoiceContainer, 'container').and.returnValue({
client: {root: '/path'} client: {root: '/path'}
}); });
spyOn(fs, 'createReadStream').and.returnValue(new Promise(resolve => resolve('streamObject'))); spyOn(fs, 'createReadStream').and.returnValue(new Promise(resolve => resolve('streamObject')));
spyOn(fs, 'access').and.returnValue(true); spyOn(fs, 'access').and.returnValue(true);
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
const result = await models.InvoiceOut.download(1); const result = await models.InvoiceOut.download(ctx, invoiceId);
expect(result[1]).toEqual('application/pdf'); expect(result[1]).toEqual('application/pdf');
expect(result[2]).toEqual('filename="1.pdf"'); expect(result[2]).toEqual('filename="2021T1111111.pdf"');
}); });
}); });

View File

@ -61,9 +61,12 @@ describe('InvoiceOut filter()', () => {
} }
}; };
const invoiceOut = await models.InvoiceOut.findById(1, null, options);
await invoiceOut.updateAttribute('hasPdf', true, options);
const result = await models.InvoiceOut.filter(ctx, {}, options); const result = await models.InvoiceOut.filter(ctx, {}, options);
expect(result.length).toEqual(5); expect(result.length).toEqual(1);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -1,5 +1,8 @@
<vn-portal slot="menu"> <vn-portal slot="menu">
<vn-invoice-out-descriptor invoice-out="$ctrl.invoiceOut"></vn-invoice-out-descriptor> <vn-invoice-out-descriptor
invoice-out="$ctrl.invoiceOut"
card-reload="$ctrl.reload()">
</vn-invoice-out-descriptor>
<vn-left-menu source="card"></vn-left-menu> <vn-left-menu source="card"></vn-left-menu>
</vn-portal> </vn-portal>
<ui-view></ui-view> <ui-view></ui-view>

View File

@ -10,7 +10,8 @@ class Controller extends ModuleCard {
'issued', 'issued',
'amount', 'amount',
'clientFk', 'clientFk',
'companyFk' 'companyFk',
'hasPdf'
], ],
include: [ include: [
{ {

View File

@ -33,11 +33,10 @@
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="createInvoicePdfConfirmation.show()" ng-click="createInvoicePdfConfirmation.show()"
vn-acl="invoicing" ng-show="$ctrl.hasInvoicing || !$ctrl.invoiceOut.hasPdf"
vn-acl-action="remove"
name="regenerateInvoice" name="regenerateInvoice"
translate> translate>
Regenerate invoice PDF {{!$ctrl.invoiceOut.hasPdf ? 'Generate PDF invoice': 'Regenerate PDF invoice'}}
</vn-item> </vn-item>
</slot-menu> </slot-menu>
<slot-body> <slot-body>
@ -101,8 +100,8 @@
<vn-confirm <vn-confirm
vn-id="createInvoicePdfConfirmation" vn-id="createInvoicePdfConfirmation"
on-accept="$ctrl.createInvoicePdf()" on-accept="$ctrl.createInvoicePdf()"
question="Are you sure you want to regenerate the invoice PDF document?" question="Are you sure you want to generate/regenerate the PDF invoice?"
message="You are going to regenerate the invoice PDF document"> message="Generate PDF invoice document">
</vn-confirm> </vn-confirm>
<!-- Send invoice confirmation popup --> <!-- Send invoice confirmation popup -->

View File

@ -10,6 +10,10 @@ class Controller extends Descriptor {
this.entity = value; this.entity = value;
} }
get hasInvoicing() {
return this.aclService.hasAny(['invoicing']);
}
deleteInvoiceOut() { deleteInvoiceOut() {
return this.$http.post(`InvoiceOuts/${this.id}/delete`) return this.$http.post(`InvoiceOuts/${this.id}/delete`)
.then(() => this.$state.go('invoiceOut.index')) .then(() => this.$state.go('invoiceOut.index'))
@ -25,6 +29,7 @@ class Controller extends Descriptor {
createInvoicePdf() { createInvoicePdf() {
const invoiceId = this.invoiceOut.id; const invoiceId = this.invoiceOut.id;
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`) return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
.then(() => this.reload())
.then(() => { .then(() => {
const snackbarMessage = this.$t( const snackbarMessage = this.$t(
`The invoice PDF document has been regenerated`); `The invoice PDF document has been regenerated`);
@ -60,6 +65,17 @@ class Controller extends Descriptor {
.then(res => this.entity = res.data); .then(res => this.entity = res.data);
} }
reload() {
return this.loadData().then(() => {
if (this.cardReload)
this.cardReload();
});
}
cardReload() {
// Prevents error when not defined
}
sendInvoice($data) { sendInvoice($data) {
return this.vnEmail.send('invoice', { return this.vnEmail.send('invoice', {
recipientId: this.invoiceOut.client.id, recipientId: this.invoiceOut.client.id,
@ -73,6 +89,7 @@ ngModule.vnComponent('vnInvoiceOutDescriptor', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
bindings: { bindings: {
invoiceOut: '<' invoiceOut: '<',
cardReload: '&'
} }
}); });

View File

@ -18,6 +18,7 @@ describe('vnInvoiceOutDescriptor', () => {
controller.invoiceOut = invoiceOut; controller.invoiceOut = invoiceOut;
$httpBackend.whenGET(`InvoiceOuts/${invoiceOut.id}`).respond();
$httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond(); $httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond();
controller.createInvoicePdf(); controller.createInvoicePdf();
$httpBackend.flush(); $httpBackend.flush();

View File

@ -12,5 +12,6 @@ Are you sure you want to clone this invoice?: Estas seguro de clonar esta factur
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 Generate PDF invoice: Generar PDF factura
Regenerate PDF invoice: Regenerar PDF factura
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado

View File

@ -1,5 +1,8 @@
<vn-portal slot="menu"> <vn-portal slot="menu">
<vn-ticket-descriptor ticket="$ctrl.ticket" card-reload="$ctrl.reload()"></vn-ticket-descriptor> <vn-ticket-descriptor
ticket="$ctrl.ticket"
card-reload="$ctrl.reload()">
</vn-ticket-descriptor>
<vn-left-menu source="card"></vn-left-menu> <vn-left-menu source="card"></vn-left-menu>
</vn-portal> </vn-portal>
<ui-view></ui-view> <ui-view></ui-view>

View File

@ -80,12 +80,10 @@
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="createInvoicePdfConfirmation.show()" ng-click="createInvoicePdfConfirmation.show()"
ng-show="$ctrl.isInvoiced" ng-show="$ctrl.isInvoiced && ($ctrl.hasInvoicing || !$ctrl.ticket.invoiceOut.hasPdf)"
vn-acl="invoicing"
vn-acl-action="remove"
name="regenerateInvoice" name="regenerateInvoice"
translate> translate>
Regenerate invoice PDF {{!$ctrl.ticket.invoiceOut.hasPdf ? 'Generate PDF invoice': 'Regenerate PDF invoice'}}
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="recalculateComponentsConfirmation.show()" ng-click="recalculateComponentsConfirmation.show()"
@ -210,8 +208,8 @@
<vn-confirm <vn-confirm
vn-id="createInvoicePdfConfirmation" vn-id="createInvoicePdfConfirmation"
on-accept="$ctrl.createInvoicePdf()" on-accept="$ctrl.createInvoicePdf()"
question="Are you sure you want to regenerate the invoice PDF document?" question="Are you sure you want to generate/regenerate the PDF invoice?"
message="You are going to regenerate the invoice PDF document"> message="Generate PDF invoice document">
</vn-confirm> </vn-confirm>
<!-- Recalculate components confirmation dialog --> <!-- Recalculate components confirmation dialog -->

View File

@ -226,6 +226,7 @@ class Controller extends Section {
createInvoicePdf() { createInvoicePdf() {
const invoiceId = this.ticket.invoiceOut.id; const invoiceId = this.ticket.invoiceOut.id;
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`) return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
.then(() => this.reload())
.then(() => { .then(() => {
const snackbarMessage = this.$t( const snackbarMessage = this.$t(
`The invoice PDF document has been regenerated`); `The invoice PDF document has been regenerated`);

View File

@ -153,6 +153,7 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
it('should make a query and show a success snackbar', () => { it('should make a query and show a success snackbar', () => {
jest.spyOn(controller.vnApp, 'showSuccess'); jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.whenGET(`Tickets/16`).respond();
$httpBackend.expectPOST(`InvoiceOuts/${ticket.invoiceOut.id}/createPdf`).respond(); $httpBackend.expectPOST(`InvoiceOuts/${ticket.invoiceOut.id}/createPdf`).respond();
controller.createInvoicePdf(); controller.createInvoicePdf();
$httpBackend.flush(); $httpBackend.flush();

View File

@ -18,6 +18,10 @@ class Controller extends Descriptor {
super.entity = value; super.entity = value;
} }
get hasInvoicing() {
return this.aclService.hasAny(['invoicing']);
}
loadData() { loadData() {
const filter = { const filter = {
include: [ include: [

View File

@ -21,8 +21,8 @@ Regenerate invoice PDF: Regenerar PDF factura
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado 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 PDF document: Vas a regenerar el documento PDF de la factura Generate PDF invoice document: Generar PDF de la factura
Are you sure you want to regenerate the invoice PDF document?: ¿Seguro que quieres regenerar el documento PDF de la factura? Are you sure you want to generate/regenerate the PDF invoice?: ¿Seguro que quieres generar/regenerar el PDF de la factura?
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