Merge branch 'dev' into 7524-addFixture
gitea/salix/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
Jorge Penadés 2024-09-05 09:57:26 +00:00
commit 4869322476
23 changed files with 623 additions and 273 deletions

View File

@ -15,9 +15,7 @@ describe('ticket assignCollection()', () => {
args: {}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: ctx.req
});
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: ctx.req});
options = {transaction: tx};
tx = await models.Sale.beginTransaction({});
@ -25,7 +23,7 @@ describe('ticket assignCollection()', () => {
});
afterEach(async() => {
await tx.rollback();
if (tx) await tx.rollback();
});
it('should throw an error when there is not picking tickets', async() => {

View File

@ -1959,7 +1959,7 @@ INSERT INTO `ACL` VALUES (746,'Claim','getSummary','READ','ALLOW','ROLE','claimV
INSERT INTO `ACL` VALUES (747,'CplusRectificationType','*','READ','ALLOW','ROLE','administrative',NULL);
INSERT INTO `ACL` VALUES (748,'SiiTypeInvoiceOut','*','READ','ALLOW','ROLE','salesPerson',NULL);
INSERT INTO `ACL` VALUES (749,'InvoiceCorrectionType','*','READ','ALLOW','ROLE','salesPerson',NULL);
INSERT INTO `ACL` VALUES (750,'InvoiceOut','transferInvoice','WRITE','ALLOW','ROLE','administrative',NULL);
INSERT INTO `ACL` VALUES (750,'InvoiceOut','transfer','WRITE','ALLOW','ROLE','administrative',NULL);
INSERT INTO `ACL` VALUES (751,'Application','executeProc','*','ALLOW','ROLE','employee',NULL);
INSERT INTO `ACL` VALUES (752,'Application','executeFunc','*','ALLOW','ROLE','employee',NULL);
INSERT INTO `ACL` VALUES (753,'NotificationSubscription','getList','READ','ALLOW','ROLE','employee',NULL);

View File

@ -1,2 +0,0 @@
CREATE USER 'vn'@'localhost';
GRANT ALL PRIVILEGES ON *.* TO 'vn'@'localhost' WITH GRANT OPTION;;

View File

@ -0,0 +1,2 @@
INSERT IGNORE INTO salix.ACL (model,property,principalId)
VALUES ('Entry','getBuysCsv','supplier');

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES ('InvoiceOut','refundAndInvoice','WRITE','ALLOW','ROLE','administrative');

View File

@ -230,10 +230,12 @@
"This workCenter is already assigned to this agency": "This workCenter is already assigned to this agency",
"You can only have one PDA": "You can only have one PDA",
"Incoterms and Customs agent are required for a non UEE member": "Incoterms and Customs agent are required for a non UEE member",
"It has been invoiced but the PDF could not be generated": "It has been invoiced but the PDF could not be generated",
"The invoices have been created but the PDFs could not be generated": "The invoices have been created but the PDFs could not be generated",
"It has been invoiced but the PDF of refund not be generated": "It has been invoiced but the PDF of refund not be generated",
"Cannot add holidays on this day": "Cannot add holidays on this day",
"Cannot send mail": "Cannot send mail",
"CONSTRAINT `chkParkingCodeFormat` failed for `vn`.`parking`": "CONSTRAINT `chkParkingCodeFormat` failed for `vn`.`parking`",
"This postcode already exists": "This postcode already exists"
"This postcode already exists": "This postcode already exists",
"Original invoice not found": "Original invoice not found"
}

View File

@ -363,12 +363,13 @@
"You can not use the same password": "No puedes usar la misma contraseña",
"This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
"You can only have one PDA": "Solo puedes tener un PDA",
"It has been invoiced but the PDF could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"The invoices have been created but the PDFs could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
"Payment method is required": "El método de pago es obligatorio",
"Cannot send mail": "Não é possível enviar o email",
"CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos",
"The sale not exists in the item shelving": "La venta no existe en la estantería del artículo",
"The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros"
"Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada"
}

View File

@ -358,7 +358,9 @@
"This workCenter is already assigned to this agency": "Ce centre de travail est déjà assigné à cette agence",
"Select ticket or client": "Choisissez un ticket ou un client",
"It was not able to create the invoice": "Il n'a pas été possible de créer la facture",
"It has been invoiced but the PDF could not be generated": "La facture a été émise mais le PDF n'a pas pu être généré",
"The invoices have been created but the PDFs could not be generated": "La facture a été émise mais le PDF n'a pas pu être généré",
"It has been invoiced but the PDF of refund not be generated": "Il a été facturé mais le PDF de remboursement n'a pas été généré",
"Cannot send mail": "Impossible d'envoyer le mail"
"Cannot send mail": "Impossible d'envoyer le mail",
"Original invoice not found": "Facture originale introuvable"
}

View File

@ -358,6 +358,8 @@
"This workCenter is already assigned to this agency": "Este centro de trabalho já está atribuído a esta agência",
"Select ticket or client": "Selecione um ticket ou cliente",
"It was not able to create the invoice": "Não foi possível criar a fatura",
"It has been invoiced but the PDF could not be generated": "Foi faturado, mas o PDF não pôde ser gerado",
"It has been invoiced but the PDF of refund not be generated": "Foi faturado mas não foi gerado o PDF do reembolso"
"The invoices have been created but the PDFs could not be generated": "Foi faturado, mas o PDF não pôde ser gerado",
"It has been invoiced but the PDF of refund not be generated": "Foi faturado mas não foi gerado o PDF do reembolso",
"Original invoice not found": "Fatura original não encontrada"
}

43
loopback/util/flatten.js Normal file
View File

@ -0,0 +1,43 @@
/**
* Flattens an array of objects by converting each object into a flat structure.
*
* @param {Array} dataArray Array of objects to be flattened
* @return {Array} Array of flattened objects
*/
function flatten(dataArray) {
return dataArray.map(item => flattenObj(item.__data));
}
/**
* Recursively flattens an object, converting nested properties into a single level object
* with keys representing the original nested structure.
*
* @param {Object} data The object to be flattened
* @param {String} [prefix=''] Optional prefix for nested keys
* @return {Object} Flattened object
*/
function flattenObj(data, prefix = '') {
let result = {};
try {
for (let key in data) {
if (!data[key]) continue;
const newKey = prefix ? `${prefix}_${key}` : key;
const value = data[key];
if (typeof value === 'object' && value !== null && !Array.isArray(value))
Object.assign(result, flattenObj(value.__data, newKey));
else
result[newKey] = value;
}
} catch (error) {
console.error(error);
}
return result;
}
module.exports = {
flatten,
flattenObj,
};

View File

@ -0,0 +1,42 @@
const {toCSV} = require('vn-loopback/util/csv');
const {flatten} = require('vn-loopback/util/flatten');
module.exports = Self => {
Self.remoteMethodCtx('getBuysCsv', {
description: 'Returns buys for one entry in CSV file format',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The entry id',
http: {source: 'path'}
}
],
returns: [
{
arg: 'body',
type: 'file',
root: true
}, {
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
}, {
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: `/:id/getBuysCsv`,
verb: 'GET'
}
});
Self.getBuysCsv = async(ctx, id, options) => {
const data = await Self.getBuys(ctx, id, null, options);
const dataFlatted = flatten(data);
return [toCSV(dataFlatted), 'text/csv', `inline; filename="buys-${id}.csv"`];
};
};

View File

@ -3,6 +3,7 @@ module.exports = Self => {
require('../methods/entry/filter')(Self);
require('../methods/entry/getEntry')(Self);
require('../methods/entry/getBuys')(Self);
require('../methods/entry/getBuysCsv')(Self);
require('../methods/entry/importBuys')(Self);
require('../methods/entry/importBuysPreview')(Self);
require('../methods/entry/lastItemBuys')(Self);

View File

@ -47,12 +47,16 @@ module.exports = Self => {
Self.invoiceClient = async(ctx, options) => {
const args = ctx.args;
const models = Self.app.models;
options = typeof options === 'object' ? {...options} : {};
options.userId = ctx.req.accessToken.userId;
let tx;
if (!options.transaction)
tx = options.transaction = await Self.beginTransaction({});
const myOptions = {userId: ctx.req.accessToken.userId};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const minShipped = Date.vnNew();
minShipped.setFullYear(args.maxShipped.getFullYear() - 1);

View File

@ -0,0 +1,89 @@
module.exports = Self => {
Self.remoteMethodCtx('refundAndInvoice', {
description: 'Refund an invoice and create a new one',
accessType: 'WRITE',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'Issued invoice id'
},
{
arg: 'cplusRectificationTypeFk',
type: 'number',
required: true
},
{
arg: 'siiTypeInvoiceOutFk',
type: 'number',
required: true
},
{
arg: 'invoiceCorrectionTypeFk',
type: 'number',
required: true
},
],
returns: {
type: 'object',
root: true
},
http: {
path: '/refundAndInvoice',
verb: 'post'
}
});
Self.refundAndInvoice = async(
ctx,
id,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
options
) => {
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
let refundId;
if (typeof options == 'object')
Object.assign(myOptions, options);
let tx;
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const originalInvoice = await models.InvoiceOut.findById(id, myOptions);
const refundedTickets = await Self.refund(ctx, originalInvoice.ref, false, myOptions);
const invoiceCorrection = {
correctedFk: id,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk
};
const ticketIds = refundedTickets.map(ticket => ticket.id);
refundId = await models.Ticket.invoiceTickets(ctx, ticketIds, invoiceCorrection, myOptions);
tx && await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
if (tx) {
try {
await models.InvoiceOut.makePdfList(ctx, refundId);
} catch (e) {
throw new UserError('The invoices have been created but the PDFs could not be generated');
}
}
return {refundId};
};
};

View File

@ -0,0 +1,105 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
const UserError = require('vn-loopback/util/user-error');
describe('InvoiceOut makePdfAndNotify()', () => {
const userId = 5;
const ctx = {
req: {
accessToken: {userId},
__: (key, obj) => `Translated: ${key}, ${JSON.stringify(obj)}`,
getLocale: () => 'es'
},
args: {}
};
const activeCtx = {accessToken: {userId}};
const id = 4;
const printerFk = 1;
beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: activeCtx});
});
it('should generate PDF and send email when client is to be mailed', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try {
await models.InvoiceOut.makePdfAndNotify(ctx, id, printerFk, options);
const invoice = await models.InvoiceOut.findById(id, {
fields: ['ref', 'clientFk'],
include: {
relation: 'client',
scope: {
fields: ['id', 'email', 'isToBeMailed', 'salesPersonFk']
}
}
}, options);
expect(invoice).toBeDefined();
expect(invoice.client().isToBeMailed).toBe(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should generate PDF and print when client is not to be mailed', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try {
await models.InvoiceOut.makePdfAndNotify(ctx, id, null, options);
const invoice = await models.InvoiceOut.findById(id, {
fields: ['ref', 'clientFk'],
include: {
relation: 'client',
scope: {
fields: ['id', 'email', 'isToBeMailed', 'salesPersonFk']
}
}
}, options);
expect(invoice).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should throw UserError when PDF generation fails', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try {
await models.InvoiceOut.makePdfAndNotify(ctx, null, null, options);
await tx.rollback();
} catch (e) {
expect(e instanceof UserError).toBe(true);
expect(e.message).toContain('Error while generating PDF');
await tx.rollback();
}
});
it('should send message to salesperson when email fails', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try {
spyOn(models.InvoiceOut, 'invoiceEmail').and.rejectWith(new Error('Test Error'));
await models.InvoiceOut.makePdfAndNotify(ctx, id, null, options);
await tx.rollback();
} catch (e) {
expect(e instanceof UserError).toBe(true);
expect(e.message).toContain('Error when sending mail to client');
await tx.rollback();
}
});
});

View File

@ -0,0 +1,46 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('InvoiceOut refundAndInvoice()', () => {
const userId = 5;
const ctx = {req: {accessToken: {userId}}};
const activeCtx = {accessToken: {userId}};
const id = 4;
const cplusRectificationTypeFk = 1;
const siiTypeInvoiceOutFk = 1;
const invoiceCorrectionTypeFk = 1;
beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: activeCtx});
});
it('should refund an invoice and create a new invoice', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
try {
const result = await models.InvoiceOut.refundAndInvoice(
ctx,
id,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
options
);
expect(result).toBeDefined();
expect(result.refundId).toBeDefined();
const invoicesAfter = await models.InvoiceOut.find({where: {id: result.refundId}}, options);
const ticketsAfter = await models.Ticket.find({where: {refFk: 'R10100001'}}, options);
expect(invoicesAfter.length).toBeGreaterThan(0);
expect(ticketsAfter.length).toBeGreaterThan(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,135 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
const UserError = require('vn-loopback/util/user-error');
describe('InvoiceOut transfer()', () => {
const userId = 5;
let options;
let tx;
let ctx;
const activeCtx = {accessToken: {userId}};
const id = 4;
const newClientFk = 1101;
const cplusRectificationTypeFk = 1;
const siiTypeInvoiceOutFk = 1;
const invoiceCorrectionTypeFk = 1;
beforeEach(async() => {
ctx = {
req: {
accessToken: {userId: 1106},
headers: {origin: 'http://localhost'},
__: value => value,
getLocale: () => 'es'
},
args: {}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({active: activeCtx});
options = {transaction: tx};
tx = await models.Sale.beginTransaction({});
options.transaction = tx;
});
afterEach(async() => {
await tx.rollback();
});
it('should transfer an invoice to a new client and return the new invoice ID', async() => {
const makeInvoice = true;
const makePdfListMock = spyOn(models.InvoiceOut, 'makePdfList').and.returnValue();
const [result] = await models.InvoiceOut.transfer(
ctx,
id,
newClientFk,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
makeInvoice,
options
);
const newInvoice = await models.InvoiceOut.findById(result, null, options);
expect(newInvoice.clientFk).toBe(newClientFk);
const transferredTickets = await models.Ticket.find({
where: {
refFk: newInvoice.ref,
clientFk: newClientFk
}
}, options);
expect(transferredTickets.length).toBeGreaterThan(0);
expect(makePdfListMock).toHaveBeenCalledWith(ctx, [result]);
});
it('should throw an error if original invoice is not found', async() => {
const makeInvoice = true;
try {
await models.InvoiceOut.transfer(
ctx,
'idNotExists',
newClientFk,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
makeInvoice,
options
);
fail('Expected an error to be thrown');
} catch (e) {
expect(e).toBeInstanceOf(UserError);
expect(e.message).toBe('Original invoice not found');
}
});
it('should throw an error if the new client is the same as the original client', async() => {
const makeInvoice = true;
const originalInvoice = await models.InvoiceOut.findById(id, options);
try {
await models.InvoiceOut.transfer(
ctx,
id,
originalInvoice.clientFk,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
makeInvoice,
options
);
fail('Expected an error to be thrown');
} catch (e) {
expect(e).toBeInstanceOf(UserError);
expect(e.message).toBe('Select a different client');
}
});
it('should not create a new invoice if makeInvoice is false', async() => {
const originalTickets = await models.Ticket.find({
where: {clientFk: newClientFk, refFk: null},
options
});
const result = await models.InvoiceOut.transfer(
ctx,
id,
newClientFk,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
false,
options
);
expect(result).toBeUndefined();
const transferredTickets = await models.Ticket.find({
where: {clientFk: newClientFk, refFk: null}
}, options);
expect(transferredTickets.length).toBeGreaterThan(originalTickets.length);
});
});

View File

@ -1,116 +0,0 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('InvoiceOut transferInvoice()', () => {
const activeCtx = {
accessToken: {userId: 5},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
const ctx = {req: activeCtx};
beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should return the id of the created issued invoice', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
const id = 4;
const newClient = 1;
spyOn(models.InvoiceOut, 'makePdfList');
try {
const {clientFk: oldClient} = await models.InvoiceOut.findById(id, {fields: ['clientFk']});
const invoicesBefore = await models.InvoiceOut.find({}, options);
const result = await models.InvoiceOut.transferInvoice(
ctx,
id,
'T4444444',
newClient,
1,
1,
1,
true,
options);
const invoicesAfter = await models.InvoiceOut.find({}, options);
const rectificativeInvoice = invoicesAfter[invoicesAfter.length - 2];
const newInvoice = invoicesAfter[invoicesAfter.length - 1];
expect(result).toBeDefined();
expect(invoicesAfter.length - invoicesBefore.length).toEqual(2);
expect(rectificativeInvoice.clientFk).toEqual(oldClient);
expect(newInvoice.clientFk).toEqual(newClient);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should throw an error when it is the same client', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
spyOn(models.InvoiceOut, 'makePdfList');
try {
await models.InvoiceOut.transferInvoice(ctx, '1', 'T1111111', 1101, 1, 1, 1, true, options);
await tx.rollback();
} catch (e) {
expect(e.message).toBe(`Select a different client`);
await tx.rollback();
}
});
it('should throw an error when it is refund', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
spyOn(models.InvoiceOut, 'makePdfList');
try {
await models.InvoiceOut.transferInvoice(ctx, '1', 'T1111111', 1102, 1, 1, 1, true, options);
await tx.rollback();
} catch (e) {
expect(e.message).toContain(`This ticket is already a refund`);
await tx.rollback();
}
});
it('should throw an error when pdf failed', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
spyOn(models.InvoiceOut, 'makePdfList').and.returnValue(() => {
throw new Error('test');
});
try {
await models.InvoiceOut.transferInvoice(ctx, '1', 'T1111111', 1102, 1, 1, 1, true, options);
await tx.rollback();
} catch (e) {
expect(e.message).toContain(`It has been invoiced but the PDF could not be generated`);
await tx.rollback();
}
});
it('should not generate an invoice', async() => {
const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx};
spyOn(models.InvoiceOut, 'makePdfList');
let response;
try {
response = await models.InvoiceOut.transferInvoice(ctx, '1', 'T1111111', 1102, 1, 1, 1, false, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(response).not.toBeDefined();
});
});

View File

@ -0,0 +1,122 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('transfer', {
description: 'Transfer an issued invoice to another client',
accessType: 'WRITE',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'Issued invoice id'
},
{
arg: 'newClientFk',
type: 'number',
required: true
},
{
arg: 'cplusRectificationTypeFk',
type: 'number',
required: true
},
{
arg: 'siiTypeInvoiceOutFk',
type: 'number',
required: true
},
{
arg: 'invoiceCorrectionTypeFk',
type: 'number',
required: true
},
{
arg: 'makeInvoice',
type: 'boolean',
required: true
},
],
returns: {type: 'object', root: true},
http: {path: '/transfer', verb: 'post'}
});
Self.transfer = async(
ctx,
id,
newClientFk,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
makeInvoice,
options
) => {
const models = Self.app.models;
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const originalInvoice = await models.InvoiceOut.findById(id);
if (!originalInvoice)
throw new UserError('Original invoice not found');
if (originalInvoice.clientFk === newClientFk)
throw new UserError('Select a different client');
let transferredInvoiceId;
try {
await Self.refundAndInvoice(
ctx,
id,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
myOptions
);
const tickets = await models.Ticket.find({where: {refFk: originalInvoice.ref}}, myOptions);
const ticketIds = tickets.map(ticket => ticket.id);
const transferredTickets = await models.Ticket.cloneAll(ctx, ticketIds, false, false, myOptions);
const client = await models.Client.findById(newClientFk,
{fields: ['id', 'defaultAddressFk']}, myOptions);
const address = await models.Address.findById(client.defaultAddressFk,
{fields: ['id', 'nickname']}, myOptions);
const transferredTicketIds = transferredTickets.map(ticket => ticket.id);
await models.Ticket.updateAll(
{id: {inq: transferredTicketIds}},
{
clientFk: newClientFk,
addressFk: client.defaultAddressFk,
nickname: address.nickname
},
myOptions
);
if (makeInvoice)
transferredInvoiceId = await models.Ticket.invoiceTickets(ctx, transferredTicketIds, null, myOptions);
if (tx) await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
if (transferredInvoiceId) {
try {
await models.InvoiceOut.makePdfList(ctx, transferredInvoiceId);
} catch (e) {
throw new UserError('The invoices have been created but the PDFs could not be generatedd');
}
}
return transferredInvoiceId;
};
};

View File

@ -1,131 +0,0 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('transferInvoice', {
description: 'Transfer an issued invoice to another client',
accessType: 'WRITE',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'Issued invoice id'
},
{
arg: 'refFk',
type: 'string',
required: true
},
{
arg: 'newClientFk',
type: 'number',
required: true
},
{
arg: 'cplusRectificationTypeFk',
type: 'number',
required: true
},
{
arg: 'siiTypeInvoiceOutFk',
type: 'number',
required: true
},
{
arg: 'invoiceCorrectionTypeFk',
type: 'number',
required: true
},
{
arg: 'makeInvoice',
type: 'boolean',
required: true
},
],
returns: {
type: 'object',
root: true
},
http: {
path: '/transferInvoice',
verb: 'post'
}
});
Self.transferInvoice = async(
ctx,
id,
refFk,
newClientFk,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk,
makeInvoice,
options
) => {
const models = Self.app.models;
const myOptions = {userId: ctx.req.accessToken.userId};
let invoiceId;
let refundId;
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
const {clientFk} = await models.InvoiceOut.findById(id);
if (clientFk == newClientFk)
throw new UserError(`Select a different client`);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const tickets = await models.Ticket.find({where: {refFk}}, myOptions);
const ticketsIds = tickets.map(ticket => ticket.id);
const refundTickets = await models.Ticket.cloneAll(ctx, ticketsIds, false, true, myOptions);
const clonedTickets = await models.Ticket.cloneAll(ctx, ticketsIds, false, false, myOptions);
const clonedTicketIds = [];
for (const clonedTicket of clonedTickets) {
await clonedTicket.updateAttribute('clientFk', newClientFk, myOptions);
clonedTicketIds.push(clonedTicket.id);
}
const invoiceCorrection = {
correctedFk: id,
cplusRectificationTypeFk,
siiTypeInvoiceOutFk,
invoiceCorrectionTypeFk
};
const refundTicketIds = refundTickets.map(ticket => ticket.id);
refundId = await models.Ticket.invoiceTickets(ctx, refundTicketIds, invoiceCorrection, myOptions);
if (makeInvoice)
invoiceId = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, null, myOptions);
tx && await tx.commit();
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
if (tx && makeInvoice) {
try {
await models.InvoiceOut.makePdfList(ctx, invoiceId);
} catch (e) {
throw new UserError('It has been invoiced but the PDF could not be generated');
}
try {
await models.InvoiceOut.makePdfList(ctx, refundId);
} catch (e) {
throw new UserError('It has been invoiced but the PDF of refund not be generated');
}
}
return invoiceId;
};
};

View File

@ -26,7 +26,8 @@ module.exports = Self => {
require('../methods/invoiceOut/getInvoiceDate')(Self);
require('../methods/invoiceOut/negativeBases')(Self);
require('../methods/invoiceOut/negativeBasesCsv')(Self);
require('../methods/invoiceOut/transferInvoice')(Self);
require('../methods/invoiceOut/transfer')(Self);
require('../methods/invoiceOut/refundAndInvoice')(Self);
Self.filePath = async function(id, options) {
const fields = ['ref', 'issued'];

View File

@ -138,7 +138,6 @@ class Controller extends Section {
transferInvoice() {
const params = {
id: this.invoiceOut.id,
refFk: this.invoiceOut.ref,
newClientFk: this.clientId,
cplusRectificationTypeFk: this.cplusRectificationType,
siiTypeInvoiceOutFk: this.siiTypeInvoiceOut,
@ -155,7 +154,7 @@ class Controller extends Section {
return;
}
this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => {
this.$http.post(`InvoiceOuts/transfer`, params).then(res => {
const invoiceId = res.data;
this.vnApp.showSuccess(this.$t('Transferred invoice'));
this.$state.go('invoiceOut.card.summary', {id: invoiceId});

View File

@ -35,8 +35,11 @@ module.exports = Self => {
Self.cloneAll = async(ctx, ticketsIds, withWarehouse, negative, options) => {
const models = Self.app.models;
const myOptions = typeof options == 'object' ? {...options} : {};
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});