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]
indent: [error, 4]
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)',
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)`,
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)',
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"]',
@ -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)`,
closeStowawayDialog: 'vn-ticket-descriptor > vn-add-stowaway > vn-dialog > div > button[class="close"]',
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"]'
},
ticketNotes: {

View File

@ -285,6 +285,8 @@ export default class DropDown extends Component {
}
let li = this.document.createElement('li');
li.setAttribute('name', option[this.showField]);
fragment.appendChild(li);
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/summary')(Self);
require('../methods/invoiceOut/download')(Self);
require('../methods/invoiceOut/regenerate')(Self);
};

View File

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

View File

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

View File

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

View File

@ -9,15 +9,34 @@ class Controller {
this.$translate = $translate;
this.aclService = aclService;
this.moreOptions = [
{callback: this.showAddTurnDialog, name: 'Add turn'},
{callback: this.showAddStowaway, name: 'Add stowaway', show: () => this.isTicketModule()},
{callback: this.showRemoveStowaway, name: 'Remove stowaway', show: () => this.shouldShowRemoveStowaway()},
{callback: this.showInvoiceOutMakeDialog, name: 'Make invoice', acl: 'invoicing'},
{callback: this.showDeliveryNote, name: 'Show Delivery Note'},
{callback: this.showDeleteTicketDialog, name: 'Delete ticket'},
{callback: this.showChangeShipped, name: 'Change shipped hour'},
{callback: this.showSMSDialog, name: 'Send SMS'},
{callback: this.openRptRoute, name: 'Show pallet report'}
{name: 'Add turn', callback: this.showAddTurnDialog},
{name: 'Show Delivery Note', callback: this.showDeliveryNote},
{name: 'Delete ticket', callback: this.showDeleteTicketDialog},
{name: 'Change shipped hour', callback: this.showChangeShipped},
{name: 'Send SMS', callback: this.showSMSDialog},
{name: 'Show pallet report', callback: this.openRptRoute},
{
name: 'Add stowaway',
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
*/
showInvoiceOutMakeDialog() {
this.$scope.invoiceMakeConfirmation.show();
showMakeInvoiceDialog() {
this.$scope.makeInvoiceConfirmation.show();
}
/**
@ -199,7 +218,7 @@ class Controller {
*
* @param {String} response - Response result
*/
makeInvoiceOut(response) {
makeInvoice(response) {
if (response === 'ACCEPT') {
const query = `/ticket/api/Tickets/${this.ticket.id}/makeInvoice`;
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'];

View File

@ -9,7 +9,7 @@ describe('Ticket Component vnTicketDescriptor', () => {
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnTicketDescriptor');
controller.ticket = {id: 2};
controller.ticket = {id: 2, invoiceOut: {id: 1}};
}));
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', () => {
spyOn(controller.$state, 'reload');
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.when('POST', `/ticket/api/Tickets/2/makeInvoice`).respond();
$httpBackend.expect('POST', `/ticket/api/Tickets/2/makeInvoice`).respond();
controller.makeInvoiceOut('ACCEPT');
controller.makeInvoice('ACCEPT');
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Ticket invoiced');
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.
Por favor, entre en la página web y efectue el pago con tarjeta. Muchas gracias.
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
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