clone invoiceIn from descriptor

This commit is contained in:
Carlos Jimenez Ruiz 2021-07-23 14:06:48 +02:00
parent 6143992238
commit abbce74100
13 changed files with 247 additions and 4 deletions

View File

@ -6,6 +6,9 @@
"table": "sage.TiposRetencion"
}
},
"log": {
"showField": "withholding"
},
"properties": {
"id": {
"type": "Number",

View File

@ -2339,6 +2339,14 @@ INSERT INTO `vn`.`duaInvoiceIn`(`id`, `duaFk`, `invoiceInFk`)
(9, 9, 9),
(10, 10, 10);
INSERT INTO `vn`.`invoiceInTax` (`invoiceInFk`, `taxCodeFk`, `taxableBase`, `expenceFk`, `foreignValue`, `taxTypeSageFk`, `transactionTypeSageFk`, `created`)
VALUES
(1, 4, 99.99, '2000000000', null, null, null, CURDATE()),
(2, 4, 999.99, '2000000000', null, null, null, CURDATE()),
(3, 4, 1000.50, '2000000000', null, null, null, CURDATE()),
(4, 4, 0.50, '2000000000', null, null, null, CURDATE()),
(5, 4, 150.50, '2000000000', null, null, null, CURDATE());
INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
SELECT `id`
FROM `vn`.`ticket` t

View File

@ -0,0 +1,120 @@
const loggable = require('vn-loopback/util/log');
module.exports = Self => {
Self.remoteMethodCtx('clone', {
description: 'Clone the invoiceIn and as many invoiceInTax and invoiceInDueDay referencing it',
accessType: 'WRITE',
accepts: {
arg: 'id',
type: 'string',
required: true,
description: 'The invoiceIn id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: '/:id/clone',
verb: 'POST'
}
});
Self.clone = async(ctx, id, options) => {
const userId = ctx.req.accessToken.userId;
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;
}
try {
const sourceInvoiceIn = await Self.findById(id, {
fields: [
'id',
'serial',
'supplierRef',
'supplierFk',
'issued',
'currencyFk',
'companyFk',
'isVatDeductible',
'withholdingSageFk',
'deductibleExpenseFk',
]
}, myOptions);
const sourceInvoiceInTax = await models.InvoiceInTax.find({where: {invoiceInFk: id}}, myOptions);
const sourceInvoiceInDueDay = await models.InvoiceInDueDay.find({where: {invoiceInFk: id}}, myOptions);
const issued = new Date(sourceInvoiceIn.issued);
issued.setMonth(issued.getMonth() + 1);
const clone = await models.InvoiceIn.create({
serial: sourceInvoiceIn.serial,
supplierRef: sourceInvoiceIn.supplierRef,
supplierFk: sourceInvoiceIn.supplierFk,
issued: issued,
currencyFk: sourceInvoiceIn.currencyFk,
companyFk: sourceInvoiceIn.companyFk,
isVatDeductible: sourceInvoiceIn.isVatDeductible,
withholdingSageFk: sourceInvoiceIn.withholdingSageFk,
deductibleExpenseFk: sourceInvoiceIn.deductibleExpenseFk,
}, myOptions);
const oldProperties = await loggable.translateValues(Self, sourceInvoiceIn);
const newProperties = await loggable.translateValues(Self, clone);
await models.InvoiceInLog.create({
originFk: clone.id,
userFk: userId,
action: 'insert',
changedModel: 'InvoiceIn',
changedModelId: clone.id,
oldInstance: oldProperties,
newInstance: newProperties
}, myOptions);
const promises = [];
for (let tax of sourceInvoiceInTax) {
promises.push(models.InvoiceInTax.create({
invoiceInFk: clone.id,
taxableBase: tax.taxableBase,
expenceFk: tax.expenceFk,
foreignValue: tax.foreignValue,
taxTypeSageFk: tax.taxTypeSageFk,
transactionTypeSageFk: tax.transactionTypeSageFk
}, myOptions));
}
for (let dueDay of sourceInvoiceInDueDay) {
const dueDated = dueDay.dueDated;
dueDated.setMonth(dueDated.getMonth() + 1);
promises.push(models.InvoiceInDueDay.create({
invoiceInFk: clone.id,
dueDated: dueDated,
bankFk: dueDay.bankFk,
amount: dueDay.amount,
foreignValue: dueDated.foreignValue,
}, myOptions));
}
await Promise.all(promises);
if (tx) await tx.commit();
return clone;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,36 @@
const models = require('vn-loopback/server/server').models;
describe('invoiceIn clone()', () => {
it('should return the cloned invoiceIn and also clone invoiceInDueDays and invoiceInTaxes if there are any referencing the invoiceIn', async() => {
const userId = 1;
const ctx = {
req: {
accessToken: {userId: userId},
headers: {origin: 'http://localhost:5000'},
}
};
const tx = await models.InvoiceIn.beginTransaction({});
const options = {transaction: tx};
try {
const clone = await models.InvoiceIn.clone(ctx, 1, options);
expect(clone.supplierRef).toEqual('1234');
const invoiceInTaxes = await models.InvoiceInTax.find({where: {invoiceInFk: clone.id}}, options);
expect(invoiceInTaxes.length).toEqual(1);
const invoiceInDueDays = await models.InvoiceInDueDay.find({where: {invoiceInFk: clone.id}}, options);
expect(invoiceInDueDays.length).toEqual(2);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -2,6 +2,9 @@
"InvoiceIn": {
"dataSource": "vn"
},
"InvoiceInTax": {
"dataSource": "vn"
},
"InvoiceInDueDay": {
"dataSource": "vn"
},

View File

@ -24,6 +24,9 @@
"amount": {
"type": "number"
},
"foreignValue": {
"type": "number"
},
"created": {
"type": "date"
}

View File

@ -0,0 +1,42 @@
{
"name": "InvoiceInTax",
"base": "VnModel",
"options": {
"mysql": {
"table": "invoiceInTax"
}
},
"properties": {
"id": {
"id": true,
"type": "number",
"description": "Identifier"
},
"invoiceInFk": {
"type": "number"
},
"taxCodeFk": {
"type": "number"
},
"taxableBase": {
"type": "number"
},
"expenceFk": {
"type": "string"
},
"foreignValue": {
"type": "number"
},
"taxTypeSageFk": {
"type": "number"
},
"transactionTypeSageFk": {
"type": "number"
},
"created": {
"type": "date"
}
}
}

View File

@ -1,4 +1,5 @@
module.exports = Self => {
require('../methods/invoice-in/filter')(Self);
require('../methods/invoice-in/summary')(Self);
require('../methods/invoice-in/clone')(Self);
};

View File

@ -12,7 +12,7 @@
"properties": {
"id": {
"id": true,
"type": "Number",
"type": "number",
"description": "Identifier"
},
"serialNumber": {
@ -36,6 +36,9 @@
"booked": {
"type": "date"
},
"isVatDeductible": {
"type": "boolean"
},
"operated": {
"type": "date"
},

View File

@ -8,6 +8,13 @@
translate>
Delete Invoice
</vn-item>
<vn-item
ng-click="cloneConfirmation.show()"
vn-acl="invoicing"
name="cloneInvoice"
translate>
Clone Invoice
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
@ -42,8 +49,16 @@
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm vn-id="deleteConfirmation" on-accept="$ctrl.deleteInvoiceIn()"
<vn-confirm
vn-id="deleteConfirmation"
on-accept="$ctrl.deleteInvoiceIn()"
question="Are you sure you want to delete this invoice?">
</vn-confirm>
<vn-supplier-descriptor-popover vn-id="supplierDescriptor">
<vn-confirm
vn-id="cloneConfirmation"
on-accept="$ctrl.cloneInvoiceIn()"
question="Are you sure you want to clone this invoice?">
</vn-confirm>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -30,6 +30,12 @@ class Controller extends Descriptor {
.then(() => this.vnApp.showSuccess(this.$t('InvoiceIn deleted')));
}
cloneInvoiceIn() {
return this.$http.post(`InvoiceIns/${this.id}/clone`)
.then(res => this.$state.go('invoiceIn.card.summary', {id: res.data.id}))
.then(() => this.vnApp.showSuccess(this.$t('InvoiceIn cloned')));
}
loadData() {
const filter = {
include: [

View File

@ -2,4 +2,5 @@ InvoiceIn: Facturas recibidas
Search invoices in by reference: Buscar facturas recibidas por referencia
Entries list: Listado de entradas
Invoice list: Listado de facturas recibidas
InvoiceIn deleted: Factura eliminada
InvoiceIn deleted: Factura eliminada
InvoiceIn cloned: Factura clonada

View File

@ -5,8 +5,10 @@ Invoice ticket list: Listado de tickets de la factura
Show invoice PDF: Ver factura en PDF
Send invoice PDF: Enviar factura en PDF
Delete Invoice: Eliminar factura
Clone Invoice: Clonar factura
InvoiceOut deleted: Factura eliminada
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
Book invoice: Asentar factura
InvoiceOut booked: Factura asentada
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?