regenerate invoice #1342
gitea/salix/dev This commit looks good Details

This commit is contained in:
Joan Sanchez 2019-04-26 08:52:43 +02:00
parent 24fedff62f
commit 16bf0b3448
12 changed files with 193 additions and 22 deletions

View File

@ -31,3 +31,4 @@ rules:
curly: [error, multi-or-nest] curly: [error, multi-or-nest]
indent: [error, 4] indent: [error, 4]
arrow-parens: [error, as-needed] arrow-parens: [error, as-needed]
jasmine/no-focused-tests: 0

View File

@ -330,7 +330,7 @@ export default {
moreMenuDeleteStowawayButton: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(5)', moreMenuDeleteStowawayButton: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(5)',
moreMenuAddToTurn: `vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(2)`, moreMenuAddToTurn: `vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(2)`,
moreMenuDeleteTicket: `vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(3)`, moreMenuDeleteTicket: `vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(3)`,
moreMenuMakeInvoice: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li:nth-child(5)', moreMenuMakeInvoice: 'vn-ticket-descriptor vn-drop-down > vn-popover ul > li[name="Make invoice"]',
addStowawayDialogSecondTicket: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog vn-table vn-tr:nth-child(2)', addStowawayDialogSecondTicket: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog vn-table vn-tr:nth-child(2)',
shipSelectButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks > vn-button-menu[icon="icon-stowaway"]', shipSelectButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks > vn-button-menu[icon="icon-stowaway"]',
shipButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks vn-icon[icon="icon-stowaway"]', shipButton: 'vn-ticket-descriptor > div > div.body > div.quicklinks vn-icon[icon="icon-stowaway"]',
@ -339,7 +339,7 @@ export default {
saturdayButton: `vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)`, saturdayButton: `vn-ticket-descriptor > vn-dialog > div > form > div.body > tpl-body > div > vn-tool-bar > vn-button:nth-child(6)`,
closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]', closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]',
acceptDeleteButton: 'vn-ticket-descriptor button[response="ACCEPT"]', acceptDeleteButton: 'vn-ticket-descriptor button[response="ACCEPT"]',
acceptInvoiceOutButton: 'vn-ticket-descriptor vn-confirm[vn-id="invoiceMakeConfirmation"] button[response="ACCEPT"]', acceptInvoiceOutButton: 'vn-ticket-descriptor vn-confirm[vn-id="makeInvoiceConfirmation"] button[response="ACCEPT"]',
acceptDeleteStowawayButton: 'vn-ticket-descriptor > vn-remove-stowaway button[response="ACCEPT"]' acceptDeleteStowawayButton: 'vn-ticket-descriptor > vn-remove-stowaway button[response="ACCEPT"]'
}, },
ticketNotes: { ticketNotes: {

View File

@ -285,6 +285,8 @@ export default class DropDown extends Component {
} }
let li = this.document.createElement('li'); let li = this.document.createElement('li');
li.setAttribute('name', option[this.showField]);
fragment.appendChild(li); fragment.appendChild(li);
if (this.multiple) { if (this.multiple) {

View File

@ -0,0 +1,48 @@
module.exports = Self => {
Self.remoteMethodCtx('regenerate', {
description: 'Sends an invoice to a regeneration queue',
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 userId = ctx.req.accessToken.userId;
const models = Self.app.models;
const invoiceReportFk = 30; // FIXME - Should be deprecated
const worker = await models.Worker.findOne({where: {userFk: userId}});
const transaction = await Self.beginTransaction({});
try {
// Remove all invoice references from tickets
const invoiceOut = await models.InvoiceOut.findById(id, {transaction});
await invoiceOut.updateAttributes({
hasPdf: false
});
// Send to print queue
await Self.rawSql(`
INSERT INTO vn.printServerQueue (reportFk, param1, workerFk)
VALUES (?, ?, ?)`, [invoiceReportFk, id, worker.id], {transaction});
await transaction.commit();
return invoiceOut;
} catch (e) {
await transaction.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,39 @@
const app = require('vn-loopback/server/server');
describe('invoiceOut regenerate()', () => {
const invoiceReportFk = 30; // FIXME - Should be deprecated
const invoiceOutId = 1;
afterAll(async done => {
const invoiceOut = await app.models.InvoiceOut.findById(invoiceOutId);
await invoiceOut.updateAttributes({hasPdf: true});
await app.models.InvoiceOut.rawSql(`
DELETE FROM vn.printServerQueue
WHERE reportFk = ?`, [invoiceReportFk]);
done();
});
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);
});
});

View File

@ -2,4 +2,5 @@ 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);
}; };

View File

@ -35,7 +35,7 @@
"type": "date" "type": "date"
}, },
"hasPdf": { "hasPdf": {
"type": "Number", "type": "Boolean",
"mysql": { "mysql": {
"columnName": "pdf" "columnName": "pdf"
} }

View File

@ -7,6 +7,7 @@ class Controller {
this.filter = { this.filter = {
include: [ include: [
{relation: 'warehouse', scope: {fields: ['name']}}, {relation: 'warehouse', scope: {fields: ['name']}},
{relation: 'invoiceOut', scope: {fields: ['id']}},
{relation: 'address'}, {relation: 'address'},
{relation: 'ship'}, {relation: 'ship'},
{relation: 'agencyMode', scope: {fields: ['name']}}, {relation: 'agencyMode', scope: {fields: ['name']}},

View File

@ -180,13 +180,22 @@
<!-- Make invoice dialog --> <!-- Make invoice dialog -->
<vn-confirm <vn-confirm
vn-id="invoiceMakeConfirmation" vn-id="makeInvoiceConfirmation"
on-response="$ctrl.makeInvoiceOut(response)" on-response="$ctrl.makeInvoice(response)"
question="You are going to invoice this ticket" question="You are going to invoice this ticket"
message="Are you sure you want to invoice this ticket?"> message="Are you sure you want to invoice this ticket?">
</vn-confirm> </vn-confirm>
<!-- Make invoice dialog --> <!-- Make invoice dialog -->
<!-- Regenerate invoice dialog -->
<vn-confirm
vn-id="regenerateInvoiceConfirmation"
on-response="$ctrl.regenerateInvoice(response)"
question="You are going to regenerate the invoice"
message="Are you sure you want to regenerate the invoice?">
</vn-confirm>
<!-- Regenerate invoice dialog -->
<!-- SMS Dialog --> <!-- SMS Dialog -->
<vn-client-sms vn-id="sms" sms="$ctrl.newSMS"></vn-client-sms> <vn-client-sms vn-id="sms" sms="$ctrl.newSMS"></vn-client-sms>
<!-- SMS Dialog --> <!-- SMS Dialog -->

View File

@ -9,15 +9,34 @@ class Controller {
this.$translate = $translate; this.$translate = $translate;
this.aclService = aclService; this.aclService = aclService;
this.moreOptions = [ this.moreOptions = [
{callback: this.showAddTurnDialog, name: 'Add turn'}, {name: 'Add turn', callback: this.showAddTurnDialog},
{callback: this.showAddStowaway, name: 'Add stowaway', show: () => this.isTicketModule()}, {name: 'Show Delivery Note', callback: this.showDeliveryNote},
{callback: this.showRemoveStowaway, name: 'Remove stowaway', show: () => this.shouldShowRemoveStowaway()}, {name: 'Delete ticket', callback: this.showDeleteTicketDialog},
{callback: this.showInvoiceOutMakeDialog, name: 'Make invoice', acl: 'invoicing'}, {name: 'Change shipped hour', callback: this.showChangeShipped},
{callback: this.showDeliveryNote, name: 'Show Delivery Note'}, {name: 'Send SMS', callback: this.showSMSDialog},
{callback: this.showDeleteTicketDialog, name: 'Delete ticket'}, {name: 'Show pallet report', callback: this.openRptRoute},
{callback: this.showChangeShipped, name: 'Change shipped hour'}, {
{callback: this.showSMSDialog, name: 'Send SMS'}, name: 'Add stowaway',
{callback: this.openRptRoute, name: 'Show pallet report'} callback: this.showAddStowaway,
show: () => this.isTicketModule()
},
{
name: 'Remove stowaway',
callback: this.showRemoveStowaway,
show: () => this.shouldShowRemoveStowaway()
},
{
name: 'Make invoice',
acl: 'invoicing',
callback: this.showMakeInvoiceDialog,
show: () => !this.hasInvoice()
},
{
name: 'Regenerate invoice',
acl: 'invoicing',
callback: this.showRegenerateInvoiceDialog,
show: () => this.hasInvoice()
},
]; ];
} }
@ -189,8 +208,8 @@ class Controller {
/** /**
* Shows an invoice confirmation * Shows an invoice confirmation
*/ */
showInvoiceOutMakeDialog() { showMakeInvoiceDialog() {
this.$scope.invoiceMakeConfirmation.show(); this.$scope.makeInvoiceConfirmation.show();
} }
/** /**
@ -199,7 +218,7 @@ class Controller {
* *
* @param {String} response - Response result * @param {String} response - Response result
*/ */
makeInvoiceOut(response) { makeInvoice(response) {
if (response === 'ACCEPT') { if (response === 'ACCEPT') {
const query = `/ticket/api/Tickets/${this.ticket.id}/makeInvoice`; const query = `/ticket/api/Tickets/${this.ticket.id}/makeInvoice`;
this.$http.post(query).then(() => { this.$http.post(query).then(() => {
@ -208,6 +227,40 @@ class Controller {
}); });
} }
} }
/**
* Shows an invoice confirmation
*/
showRegenerateInvoiceDialog() {
this.$scope.regenerateInvoiceConfirmation.show();
}
/**
* Sends an invoice to a regeneration queue
* for the current ticket
*
* @param {String} response - Response result
*/
regenerateInvoice(response) {
if (response === 'ACCEPT') {
const invoiceId = this.ticket.invoiceOut.id;
const query = `/invoiceOut/api/InvoiceOuts/${invoiceId}/regenerate`;
this.$http.post(query).then(() => {
const snackbarMessage = this.$translate.instant(
`Invoice sent for a regeneration, will be available in a few minutes`);
this.vnApp.showSuccess(snackbarMessage);
});
}
}
/**
* Returns if the current ticket
* is already invoiced
* @return {Boolean} - True if invoiced
*/
hasInvoice() {
return this.ticket.refFk !== null;
}
} }
Controller.$inject = ['$state', '$scope', '$http', 'vnApp', '$translate', 'aclService']; Controller.$inject = ['$state', '$scope', '$http', 'vnApp', '$translate', 'aclService'];

View File

@ -9,7 +9,7 @@ describe('Ticket Component vnTicketDescriptor', () => {
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => { beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
controller = $componentController('vnTicketDescriptor'); controller = $componentController('vnTicketDescriptor');
controller.ticket = {id: 2}; controller.ticket = {id: 2, invoiceOut: {id: 1}};
})); }));
describe('showAddTurnDialog()', () => { describe('showAddTurnDialog()', () => {
@ -78,19 +78,32 @@ describe('Ticket Component vnTicketDescriptor', () => {
}); });
}); });
describe('invoiceMakeOut(response)', () => { describe('makeInvoice(response)', () => {
it('should make a query and call $state.reload() method if the response is ACCEPT', () => { it('should make a query and call $state.reload() method if the response is ACCEPT', () => {
spyOn(controller.$state, 'reload'); spyOn(controller.$state, 'reload');
spyOn(controller.vnApp, 'showSuccess'); spyOn(controller.vnApp, 'showSuccess');
$httpBackend.when('POST', `/ticket/api/Tickets/2/makeInvoice`).respond(); $httpBackend.when('POST', `/ticket/api/Tickets/2/makeInvoice`).respond();
$httpBackend.expect('POST', `/ticket/api/Tickets/2/makeInvoice`).respond(); $httpBackend.expect('POST', `/ticket/api/Tickets/2/makeInvoice`).respond();
controller.makeInvoiceOut('ACCEPT'); controller.makeInvoice('ACCEPT');
$httpBackend.flush(); $httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Ticket invoiced'); expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Ticket invoiced');
expect(controller.$state.reload).toHaveBeenCalledWith(); expect(controller.$state.reload).toHaveBeenCalledWith();
}); });
}); });
describe('regenerateInvoice(response)', () => {
it('should make a query and show a success snackbar if the response is ACCEPT', () => {
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.when('POST', `/invoiceOut/api/InvoiceOuts/1/regenerate`).respond();
$httpBackend.expect('POST', `/invoiceOut/api/InvoiceOuts/1/regenerate`).respond();
controller.regenerateInvoice('ACCEPT');
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Invoice sent for a regeneration, will be available in a few minutes');
});
});
}); });

View File

@ -15,6 +15,10 @@ SMSPayment: >-
Verdnatura le comunica: Su pedido está pendiente de pago. Verdnatura le comunica: Su pedido está pendiente de pago.
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias. Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.
Ticket invoiced: Ticket facturado Ticket invoiced: Ticket facturado
Make invoice: Facturar Make invoice: Crear factura
Regenerate invoice: Regenerar factura
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
Are you sure you want to regenerate the invoice?: ¿Seguro que quieres regenerar 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