This commit is contained in:
parent
a6c084fc53
commit
bed34c1750
|
@ -98,7 +98,7 @@ module.exports = function(Self) {
|
||||||
${address ? `AND addressFk = ${address}` : ''}
|
${address ? `AND addressFk = ${address}` : ''}
|
||||||
`, [ticketsIds], myOptions);
|
`, [ticketsIds], myOptions);
|
||||||
|
|
||||||
const invoiceId = await models.Ticket.makeInvoice(ctx, 'R', companyId, null, myOptions);
|
const invoiceId = await models.Ticket.makeInvoice(ctx, 'R', companyId, Date.vnNew(), myOptions);
|
||||||
invoicesIds.push(invoiceId);
|
invoicesIds.push(invoiceId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,8 @@ module.exports = function(Self) {
|
||||||
{
|
{
|
||||||
arg: 'invoiceDate',
|
arg: 'invoiceDate',
|
||||||
description: 'The invoice date',
|
description: 'The invoice date',
|
||||||
type: 'date'
|
type: 'date',
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -33,7 +34,7 @@ module.exports = function(Self) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.makeInvoice = async(ctx, invoiceType, companyFk, invoiceDate = Date.vnNew(), options) => {
|
Self.makeInvoice = async(ctx, invoiceType, companyFk, invoiceDate, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
invoiceDate.setHours(0, 0, 0, 0);
|
invoiceDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
|
|
@ -1,61 +1,83 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const LoopBackContext = require('loopback-context');
|
|
||||||
|
|
||||||
describe('ticket canBeInvoiced()', () => {
|
describe('ticket canBeInvoiced()', () => {
|
||||||
const userId = 19;
|
const userId = 19;
|
||||||
const ticketId = 11;
|
const ticketId = 11;
|
||||||
const activeCtx = {
|
const ctx = {req: {accessToken: {userId: userId}}};
|
||||||
accessToken: {userId: userId}
|
ctx.req.__ = value => {
|
||||||
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeAll(async() => {
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
|
||||||
active: activeCtx
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return falsy for an already invoiced ticket', async() => {
|
it('should return falsy for an already invoiced ticket', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(ticketId, null, options);
|
const ticket = await models.Ticket.findById(ticketId, null, options);
|
||||||
await ticket.updateAttribute('refFk', 'T1111111', options);
|
await ticket.updateAttribute('refFk', 'T1111111', options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced([ticketId], options);
|
await models.Ticket.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketToInvoice;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketToInvoice
|
||||||
|
(PRIMARY KEY (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id
|
||||||
|
FROM vn.ticket
|
||||||
|
WHERE id IN (?)
|
||||||
|
`, [ticketId], options);
|
||||||
|
|
||||||
|
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(false);
|
expect(canBeInvoiced).toEqual(false);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`This ticket is already invoiced`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return falsy for a ticket with a price of zero', async() => {
|
it('should return falsy for a ticket with a price of zero', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(ticketId, null, options);
|
const ticket = await models.Ticket.findById(ticketId, null, options);
|
||||||
await ticket.updateAttribute('totalWithVat', 0, options);
|
await ticket.updateAttribute('totalWithVat', 0, options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced([ticketId], options);
|
await models.Ticket.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketToInvoice;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketToInvoice
|
||||||
|
(PRIMARY KEY (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id
|
||||||
|
FROM vn.ticket
|
||||||
|
WHERE id IN (?)
|
||||||
|
`, [ticketId], options);
|
||||||
|
|
||||||
|
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(false);
|
expect(canBeInvoiced).toEqual(false);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`A ticket with an amount of zero can't be invoiced`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return falsy for a ticket shipping in future', async() => {
|
it('should return falsy for a ticket shipping in future', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
@ -66,15 +88,27 @@ describe('ticket canBeInvoiced()', () => {
|
||||||
|
|
||||||
await ticket.updateAttribute('shipped', shipped, options);
|
await ticket.updateAttribute('shipped', shipped, options);
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced([ticketId], options);
|
await models.Ticket.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketToInvoice;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketToInvoice
|
||||||
|
(PRIMARY KEY (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id
|
||||||
|
FROM vn.ticket
|
||||||
|
WHERE id IN (?)
|
||||||
|
`, [ticketId], options);
|
||||||
|
|
||||||
|
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(false);
|
expect(canBeInvoiced).toEqual(false);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`Can't invoice to future`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return truthy for an invoiceable ticket', async() => {
|
it('should return truthy for an invoiceable ticket', async() => {
|
||||||
|
@ -83,7 +117,17 @@ describe('ticket canBeInvoiced()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const canBeInvoiced = await models.Ticket.canBeInvoiced([ticketId], options);
|
await models.Ticket.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketToInvoice;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketToInvoice
|
||||||
|
(PRIMARY KEY (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id
|
||||||
|
FROM vn.ticket
|
||||||
|
WHERE id IN (?)
|
||||||
|
`, [ticketId], options);
|
||||||
|
|
||||||
|
const canBeInvoiced = await models.Ticket.canBeInvoiced(ctx, [ticketId], options);
|
||||||
|
|
||||||
expect(canBeInvoiced).toEqual(true);
|
expect(canBeInvoiced).toEqual(true);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('ticket invoiceTickets()', () => {
|
||||||
|
const userId = 19;
|
||||||
|
const clientId = 1102;
|
||||||
|
const activeCtx = {
|
||||||
|
getLocale: () => {
|
||||||
|
return 'en';
|
||||||
|
},
|
||||||
|
accessToken: {userId: userId},
|
||||||
|
headers: {origin: 'http://localhost:5000'},
|
||||||
|
};
|
||||||
|
const ctx = {req: activeCtx};
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when invoicing tickets from multiple clients', async() => {
|
||||||
|
const invoiceOutModel = models.InvoiceOut;
|
||||||
|
spyOn(invoiceOutModel, 'makePdfAndNotify');
|
||||||
|
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ticketsIds = [11, 16];
|
||||||
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`You can't invoice tickets from multiple clients`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should throw an error when invoicing a client without tax data checked`, async() => {
|
||||||
|
const invoiceOutModel = models.InvoiceOut;
|
||||||
|
spyOn(invoiceOutModel, 'makePdfAndNotify');
|
||||||
|
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const client = await models.Client.findById(clientId, null, options);
|
||||||
|
await client.updateAttribute('isTaxDataChecked', false, options);
|
||||||
|
|
||||||
|
const ticketsIds = [11];
|
||||||
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`This client can't be invoiced`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoice a ticket, then try again to fail', async() => {
|
||||||
|
const invoiceOutModel = models.InvoiceOut;
|
||||||
|
spyOn(invoiceOutModel, 'makePdfAndNotify');
|
||||||
|
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ticketsIds = [11];
|
||||||
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
||||||
|
await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`This ticket is already invoiced`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should success to invoice a ticket', async() => {
|
||||||
|
const invoiceOutModel = models.InvoiceOut;
|
||||||
|
spyOn(invoiceOutModel, 'makePdfAndNotify');
|
||||||
|
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const ticketsIds = [11];
|
||||||
|
const invoicesIds = await models.Ticket.invoiceTickets(ctx, ticketsIds, options);
|
||||||
|
|
||||||
|
expect(invoicesIds.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -3,8 +3,9 @@ const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('ticket makeInvoice()', () => {
|
describe('ticket makeInvoice()', () => {
|
||||||
const userId = 19;
|
const userId = 19;
|
||||||
const ticketId = 11;
|
const invoiceType = 'R';
|
||||||
const clientId = 1102;
|
const companyFk = 442;
|
||||||
|
const invoiceDate = Date.vnNew();
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
getLocale: () => {
|
getLocale: () => {
|
||||||
return 'en';
|
return 'en';
|
||||||
|
@ -20,77 +21,6 @@ describe('ticket makeInvoice()', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error when invoicing tickets from multiple clients', async() => {
|
|
||||||
const invoiceOutModel = models.InvoiceOut;
|
|
||||||
spyOn(invoiceOutModel, 'createPdf');
|
|
||||||
|
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
const otherClientTicketId = 16;
|
|
||||||
await models.Ticket.makeInvoice(ctx, [ticketId, otherClientTicketId], options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
await tx.rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(`You can't invoice tickets from multiple clients`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should throw an error when invoicing a client without tax data checked`, async() => {
|
|
||||||
const invoiceOutModel = models.InvoiceOut;
|
|
||||||
spyOn(invoiceOutModel, 'createPdf');
|
|
||||||
|
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
const client = await models.Client.findById(clientId, null, options);
|
|
||||||
await client.updateAttribute('isTaxDataChecked', false, options);
|
|
||||||
|
|
||||||
await models.Ticket.makeInvoice(ctx, [ticketId], options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
await tx.rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(`This client can't be invoiced`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should invoice a ticket, then try again to fail', async() => {
|
|
||||||
const invoiceOutModel = models.InvoiceOut;
|
|
||||||
spyOn(invoiceOutModel, 'createPdf');
|
|
||||||
spyOn(invoiceOutModel, 'invoiceEmail');
|
|
||||||
|
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
await models.Ticket.makeInvoice(ctx, [ticketId], options);
|
|
||||||
await models.Ticket.makeInvoice(ctx, [ticketId], options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
await tx.rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toEqual(`Some of the selected tickets are not billable`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should success to invoice a ticket', async() => {
|
it('should success to invoice a ticket', async() => {
|
||||||
const invoiceOutModel = models.InvoiceOut;
|
const invoiceOutModel = models.InvoiceOut;
|
||||||
spyOn(invoiceOutModel, 'createPdf');
|
spyOn(invoiceOutModel, 'createPdf');
|
||||||
|
@ -101,10 +31,20 @@ describe('ticket makeInvoice()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const invoice = await models.Ticket.makeInvoice(ctx, [ticketId], options);
|
const ticketsIds = [11, 16];
|
||||||
|
await models.Ticket.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketToInvoice;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketToInvoice
|
||||||
|
(PRIMARY KEY (id))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id
|
||||||
|
FROM vn.ticket
|
||||||
|
WHERE id IN (?)
|
||||||
|
`, [ticketsIds], options);
|
||||||
|
|
||||||
expect(invoice.invoiceFk).toBeDefined();
|
const invoiceId = await models.Ticket.makeInvoice(ctx, invoiceType, companyFk, invoiceDate, options);
|
||||||
expect(invoice.serial).toEqual('T');
|
|
||||||
|
expect(invoiceId).toBeDefined();
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -191,7 +191,7 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
const expectedParams = {ticketsIds: [ticket.id]};
|
const expectedParams = {ticketsIds: [ticket.id]};
|
||||||
$httpBackend.expectPOST(`Tickets/makeInvoice`, expectedParams).respond();
|
$httpBackend.expectPOST(`Tickets/invoiceTickets`, expectedParams).respond();
|
||||||
controller.makeInvoice();
|
controller.makeInvoice();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue