#7346 - manualInvoice #3060
|
@ -0,0 +1,56 @@
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`invoiceOut_newFromAddress`(
|
||||||
|
IN vAddressFk INT,
|
||||||
|
IN vSerial CHAR(2),
|
||||||
|
IN vMaxShipped DATE,
|
||||||
|
IN vCompanyFk INT,
|
||||||
|
IN vTaxArea VARCHAR(25),
|
||||||
|
IN vRef VARCHAR(25),
|
||||||
|
OUT vInvoiceId INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Factura los tickets de un consignatario hasta una fecha dada
|
||||||
|
* @param vAddressFk Id del consignatario a facturar
|
||||||
|
* @param vSerial Serie de factura
|
||||||
|
* @param vMaxShipped Fecha hasta la cual cogerá tickets para facturar
|
||||||
|
* @param vCompanyFk Id de la empresa desde la que se factura
|
||||||
|
* @param vTaxArea Tipo de iva en relacion a la empresa y al cliente, NULL por defecto
|
||||||
|
* @param vRef Referencia de la factura en caso que se quiera forzar, NULL por defecto
|
||||||
|
* @return vInvoiceId factura
|
||||||
|
*/
|
||||||
|
DECLARE vIsRefEditable BOOLEAN;
|
||||||
|
|
||||||
|
IF vRef IS NOT NULL AND vSerial IS NOT NULL THEN
|
||||||
|
SELECT isRefEditable INTO vIsRefEditable
|
||||||
|
FROM invoiceOutSerial
|
||||||
|
WHERE code = vSerial;
|
||||||
|
|
||||||
|
IF NOT vIsRefEditable THEN
|
||||||
|
CALL util.throw('serial non editable');
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS `tmp`.`ticketToInvoice`;
|
||||||
|
CREATE TEMPORARY TABLE `tmp`.`ticketToInvoice`
|
||||||
|
(PRIMARY KEY (`id`))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT id FROM ticket t
|
||||||
|
WHERE t.addressFk = vAddressFk
|
||||||
|
AND t.refFk IS NULL
|
||||||
|
AND t.companyFk = vCompanyFk
|
||||||
|
AND t.shipped BETWEEN
|
||||||
|
util.firstDayOfYear(vMaxShipped - INTERVAL 1 YEAR)
|
||||||
|
AND util.dayend(vMaxShipped);
|
||||||
|
|
||||||
|
CALL invoiceOut_new(vSerial, util.VN_CURDATE(), vTaxArea, vInvoiceId);
|
||||||
|
|
||||||
|
UPDATE invoiceOut
|
||||||
|
SET `ref` = vRef
|
||||||
|
WHERE id = vInvoiceId
|
||||||
|
AND vRef IS NOT NULL;
|
||||||
|
|
||||||
|
IF vSerial <> 'R' AND NOT ISNULL(vInvoiceId) AND vInvoiceId <> 0 THEN
|
||||||
|
CALL invoiceOutBooking(vInvoiceId);
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -10,6 +10,11 @@ module.exports = Self => {
|
||||||
type: 'any',
|
type: 'any',
|
||||||
description: 'The invoiceable client id'
|
description: 'The invoiceable client id'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
arg: 'addressFk',
|
||||||
|
type: 'any',
|
||||||
|
description: 'The address id'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
arg: 'ticketFk',
|
arg: 'ticketFk',
|
||||||
type: 'any',
|
type: 'any',
|
||||||
|
@ -23,7 +28,8 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'serial',
|
arg: 'serial',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The invoice serial'
|
description: 'The invoice serial',
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'taxArea',
|
arg: 'taxArea',
|
||||||
|
@ -46,108 +52,126 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.createManualInvoice = async(ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options) => {
|
Self.createManualInvoice =
|
||||||
if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`);
|
async(ctx, clientFk, addressFk, ticketFk, maxShipped, serial, taxArea, reference, options) => {
|
||||||
const models = Self.app.models;
|
if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`);
|
||||||
|
|||||||
const myOptions = {userId: ctx.req.accessToken.userId};
|
const models = Self.app.models;
|
||||||
let tx;
|
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||||
|
let tx;
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
if (!myOptions.transaction) {
|
if (!myOptions.transaction) {
|
||||||
tx = await Self.beginTransaction({});
|
tx = await Self.beginTransaction({});
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
let companyId;
|
let companyFk;
|
||||||
let newInvoice;
|
let newInvoice;
|
||||||
let query;
|
let query;
|
||||||
try {
|
try {
|
||||||
if (ticketFk) {
|
if (ticketFk) {
|
||||||
const ticket = await models.Ticket.findById(ticketFk, null, myOptions);
|
const ticket = await models.Ticket.findById(ticketFk, {
|
||||||
const company = await models.Company.findById(ticket.companyFk, null, myOptions);
|
fields: ['clientFk', 'companyFk', 'shipped', 'refFk', 'totalWithVat']
|
||||||
|
}, myOptions);
|
||||||
|
const company = await models.Company.findById(ticket.companyFk, {
|
||||||
|
fields: ['code']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
clientFk = ticket.clientFk;
|
clientFk = ticket.clientFk;
|
||||||
maxShipped = ticket.shipped;
|
maxShipped = ticket.shipped;
|
||||||
companyId = ticket.companyFk;
|
companyFk = ticket.companyFk;
|
||||||
|
|
||||||
// Validates invoiced ticket
|
if (ticket.refFk)
|
||||||
if (ticket.refFk)
|
throw new UserError('This ticket is already invoiced');
|
||||||
throw new UserError('This ticket is already invoiced');
|
|
||||||
|
|
||||||
// Validates ticket amount
|
if (ticket.totalWithVat == 0)
|
||||||
if (ticket.totalWithVat == 0)
|
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
|
||||||
throw new UserError(`A ticket with an amount of zero can't be invoiced`);
|
|
||||||
|
|
||||||
// Validates ticket nagative base
|
const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyFk, myOptions);
|
||||||
const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyId, myOptions);
|
if (hasNegativeBase && company.code == 'VNL')
|
||||||
if (hasNegativeBase && company.code == 'VNL')
|
throw new UserError(`A ticket with a negative base can't be invoiced`);
|
||||||
throw new UserError(`A ticket with a negative base can't be invoiced`);
|
} else {
|
||||||
} else {
|
if (!maxShipped)
|
||||||
if (!maxShipped)
|
throw new UserError(`Max shipped required`);
|
||||||
throw new UserError(`Max shipped required`);
|
|
||||||
|
|
||||||
const company = await models.Ticket.findOne({
|
if (addressFk) {
|
||||||
fields: ['companyFk'],
|
const address = await models.Address.findById(addressFk, {
|
||||||
where: {
|
fields: ['clientFk']
|
||||||
clientFk: clientFk,
|
}, myOptions);
|
||||||
shipped: {lte: maxShipped}
|
|
||||||
|
if (clientFk && clientFk !== address.clientFk)
|
||||||
|
throw new UserError('The provided clientFk does not match');
|
||||||
}
|
}
|
||||||
}, myOptions);
|
const company = await models.Ticket.findOne({
|
||||||
companyId = company.companyFk;
|
fields: ['companyFk'],
|
||||||
|
where: {
|
||||||
|
clientFk: clientFk,
|
||||||
|
shipped: {lte: maxShipped}
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
companyFk = company.companyFk;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isClientInvoiceable = await isInvoiceable(clientFk, myOptions);
|
||||||
|
if (!isClientInvoiceable)
|
||||||
|
throw new UserError(`This client is not invoiceable`);
|
||||||
|
|
||||||
|
const tomorrow = Date.vnNew();
|
||||||
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
|
|
||||||
|
if (maxShipped >= tomorrow)
|
||||||
|
throw new UserError(`Can't invoice to future`);
|
||||||
|
|
||||||
|
const maxInvoiceDate = await getMaxIssued(serial, companyFk, myOptions);
|
||||||
|
if (Date.vnNew() < maxInvoiceDate)
|
||||||
|
throw new UserError(`Can't invoice to past`);
|
||||||
|
|
||||||
|
if (ticketFk) {
|
||||||
|
query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`;
|
||||||
|
await Self.rawSql(query, [
|
||||||
|
ticketFk,
|
||||||
|
serial,
|
||||||
|
taxArea,
|
||||||
|
reference
|
||||||
|
], myOptions);
|
||||||
|
} else if (addressFk) {
|
||||||
|
query = `CALL invoiceOut_newFromAddress(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
|
||||||
|
await Self.rawSql(query, [
|
||||||
|
addressFk,
|
||||||
|
serial,
|
||||||
|
maxShipped,
|
||||||
|
companyFk,
|
||||||
|
taxArea,
|
||||||
|
reference
|
||||||
|
], myOptions);
|
||||||
|
} else {
|
||||||
|
query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
|
||||||
|
await Self.rawSql(query, [
|
||||||
|
clientFk,
|
||||||
|
serial,
|
||||||
|
maxShipped,
|
||||||
|
companyFk,
|
||||||
|
taxArea,
|
||||||
|
reference
|
||||||
|
], myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[newInvoice] = await Self.rawSql(`SELECT @newInvoiceId id`, null, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate invoiceable client
|
if (!newInvoice.id) throw new UserError('It was not able to create the invoice');
|
||||||
const isClientInvoiceable = await isInvoiceable(clientFk, myOptions);
|
|
||||||
if (!isClientInvoiceable)
|
|
||||||
throw new UserError(`This client is not invoiceable`);
|
|
||||||
|
|
||||||
// Can't invoice tickets into future
|
await Self.createPdf(ctx, newInvoice.id);
|
||||||
const tomorrow = Date.vnNew();
|
|
||||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
||||||
|
|
||||||
if (maxShipped >= tomorrow)
|
return newInvoice;
|
||||||
throw new UserError(`Can't invoice to future`);
|
};
|
||||||
|
|
||||||
const maxInvoiceDate = await getMaxIssued(serial, companyId, myOptions);
|
|
||||||
if (Date.vnNew() < maxInvoiceDate)
|
|
||||||
throw new UserError(`Can't invoice to past`);
|
|
||||||
|
|
||||||
if (ticketFk) {
|
|
||||||
query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`;
|
|
||||||
await Self.rawSql(query, [
|
|
||||||
ticketFk,
|
|
||||||
serial,
|
|
||||||
taxArea,
|
|
||||||
reference
|
|
||||||
], myOptions);
|
|
||||||
} else {
|
|
||||||
query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`;
|
|
||||||
await Self.rawSql(query, [
|
|
||||||
clientFk,
|
|
||||||
serial,
|
|
||||||
maxShipped,
|
|
||||||
companyId,
|
|
||||||
taxArea,
|
|
||||||
reference
|
|
||||||
], myOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
[newInvoice] = await Self.rawSql(`SELECT @newInvoiceId id`, null, myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
|
||||||
} catch (e) {
|
|
||||||
if (tx) await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newInvoice.id) throw new UserError('It was not able to create the invoice');
|
|
||||||
|
|
||||||
await Self.createPdf(ctx, newInvoice.id);
|
|
||||||
|
|
||||||
return newInvoice;
|
|
||||||
};
|
|
||||||
|
|
||||||
async function isInvoiceable(clientFk, options) {
|
async function isInvoiceable(clientFk, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
@ -159,10 +183,10 @@ module.exports = Self => {
|
||||||
return result.invoiceable;
|
return result.invoiceable;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNegativeBase(maxShipped, clientFk, companyId, options) {
|
async function getNegativeBase(maxShipped, clientFk, companyFk, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
|
await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)',
|
||||||
[maxShipped, clientFk, companyId], options
|
[maxShipped, clientFk, companyFk], options
|
||||||
);
|
);
|
||||||
const query = 'SELECT vn.hasAnyNegativeBase() AS base';
|
const query = 'SELECT vn.hasAnyNegativeBase() AS base';
|
||||||
const [result] = await models.InvoiceOut.rawSql(query, [], options);
|
const [result] = await models.InvoiceOut.rawSql(query, [], options);
|
||||||
|
@ -170,14 +194,14 @@ module.exports = Self => {
|
||||||
return result.base;
|
return result.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMaxIssued(serial, companyId, options) {
|
async function getMaxIssued(serial, companyFk, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const query = `SELECT MAX(issued) AS issued
|
const query = `SELECT MAX(issued) AS issued
|
||||||
FROM invoiceOut
|
FROM invoiceOut
|
||||||
WHERE serial = ? AND companyFk = ?`;
|
WHERE serial = ? AND companyFk = ?`;
|
||||||
const [maxIssued] = await models.InvoiceOut.rawSql(query,
|
const [maxIssued] = await models.InvoiceOut.rawSql(query,
|
||||||
[serial, companyId], options);
|
[serial, companyFk], options);
|
||||||
const maxInvoiceDate = maxIssued && maxIssued.issued || Date.vnNew();
|
const maxInvoiceDate = maxIssued?.issued || Date.vnNew();
|
||||||
|
|
||||||
return maxInvoiceDate;
|
return maxInvoiceDate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,110 +6,90 @@ describe('InvoiceOut createManualInvoice()', () => {
|
||||||
const clientId = 1106;
|
const clientId = 1106;
|
||||||
const activeCtx = {accessToken: {userId: 1}};
|
const activeCtx = {accessToken: {userId: 1}};
|
||||||
const ctx = {req: activeCtx};
|
const ctx = {req: activeCtx};
|
||||||
|
let tx; let options;
|
||||||
|
|
||||||
|
beforeEach(async() => {
|
||||||
|
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(Promise.resolve(true));
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
|
||||||
|
tx = await models.InvoiceOut.beginTransaction({});
|
||||||
|
options = {transaction: tx};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async() => {
|
||||||
|
await tx.rollback();
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw an error trying to invoice again', async() => {
|
it('should throw an error trying to invoice again', async() => {
|
||||||
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
|
|
||||||
|
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
await createInvoice(ctx, options, undefined, ticketId);
|
await createInvoice(ctx, options, undefined, undefined, ticketId);
|
||||||
await createInvoice(ctx, options, undefined, ticketId);
|
await createInvoice(ctx, options, undefined, undefined, ticketId);
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
await tx.rollback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toContain('This ticket is already invoiced');
|
expect(error.message).toContain('This ticket is already invoiced');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error for a ticket with an amount of zero', async() => {
|
it('should throw an error for a ticket with an amount of zero', async() => {
|
||||||
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
|
||||||
active: activeCtx
|
|
||||||
});
|
|
||||||
|
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
const ticket = await models.Ticket.findById(ticketId, null, options);
|
const ticket = await models.Ticket.findById(ticketId, null, options);
|
||||||
await ticket.updateAttributes({totalWithVat: 0}, options);
|
await ticket.updateAttributes({totalWithVat: 0}, options);
|
||||||
|
|
||||||
await createInvoice(ctx, options, undefined, ticketId);
|
await createInvoice(ctx, options, undefined, undefined, ticketId);
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
await tx.rollback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toContain(`A ticket with an amount of zero can't be invoiced`);
|
expect(error.message).toContain(`A ticket with an amount of zero can't be invoiced`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error when the clientFk property is set without the max shipped date', async() => {
|
it('should throw an error when the clientFk property is set without the max shipped date', async() => {
|
||||||
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
|
|
||||||
|
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
await createInvoice(ctx, options, clientId);
|
await createInvoice(ctx, options, clientId);
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
await tx.rollback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toContain(`Max shipped required`);
|
expect(error.message).toContain(`Max shipped required`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error for a non-invoiceable client', async() => {
|
it('should throw an error for a non-invoiceable client', async() => {
|
||||||
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
|
||||||
active: activeCtx
|
|
||||||
});
|
|
||||||
|
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
const client = await models.Client.findById(clientId, null, options);
|
const client = await models.Client.findById(clientId, null, options);
|
||||||
await client.updateAttributes({isTaxDataChecked: false}, options);
|
await client.updateAttributes({isTaxDataChecked: false}, options);
|
||||||
|
|
||||||
await createInvoice(ctx, options, undefined, ticketId);
|
await createInvoice(ctx, options, undefined, undefined, ticketId);
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
await tx.rollback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toContain(`This client is not invoiceable`);
|
expect(error.message).toContain(`This client is not invoiceable`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a manual invoice', async() => {
|
it('should create a manual invoice with ticket', async() => {
|
||||||
spyOn(models.InvoiceOut, 'createPdf').and.returnValue(new Promise(resolve => resolve(true)));
|
const result = await createInvoice(ctx, options, undefined, undefined, ticketId);
|
||||||
|
|
||||||
const tx = await models.InvoiceOut.beginTransaction({});
|
expect(result.id).toEqual(jasmine.any(Number));
|
||||||
const options = {transaction: tx};
|
});
|
||||||
|
|
||||||
try {
|
it('should create a manual invoice with client', async() => {
|
||||||
const result = await createInvoice(ctx, options, undefined, ticketId);
|
const result = await createInvoice(ctx, options, clientId, undefined, undefined, Date.vnNew());
|
||||||
|
|
||||||
expect(result.id).toEqual(jasmine.any(Number));
|
expect(result.id).toEqual(jasmine.any(Number));
|
||||||
|
});
|
||||||
|
|
||||||
await tx.rollback();
|
it('should create a manual invoice with address', async() => {
|
||||||
} catch (e) {
|
const addressFk = 126;
|
||||||
await tx.rollback();
|
const result = await createInvoice(ctx, options, clientId, addressFk, undefined, Date.vnNew());
|
||||||
throw e;
|
|
||||||
}
|
expect(result.id).toEqual(jasmine.any(Number));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -117,6 +97,7 @@ function createInvoice(
|
||||||
ctx,
|
ctx,
|
||||||
options,
|
options,
|
||||||
clientFk = undefined,
|
clientFk = undefined,
|
||||||
|
addressFk = undefined,
|
||||||
ticketFk = undefined,
|
ticketFk = undefined,
|
||||||
maxShipped = undefined,
|
maxShipped = undefined,
|
||||||
serial = 'T',
|
serial = 'T',
|
||||||
|
@ -124,6 +105,6 @@ function createInvoice(
|
||||||
reference = undefined
|
reference = undefined
|
||||||
) {
|
) {
|
||||||
return models.InvoiceOut.createManualInvoice(
|
return models.InvoiceOut.createManualInvoice(
|
||||||
ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options
|
ctx, clientFk, addressFk, ticketFk, maxShipped, serial, taxArea, reference, options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Se li pot pasar address sense clientFk?
No, pero tant si es pasa buit com si es pasa un que no correspon te una validació posterior.