diff --git a/back/models/sage-withholding.json b/back/models/sage-withholding.json
index 8d93daeae..dddbcfd74 100644
--- a/back/models/sage-withholding.json
+++ b/back/models/sage-withholding.json
@@ -6,6 +6,9 @@
"table": "sage.TiposRetencion"
}
},
+ "log": {
+ "showField": "withholding"
+ },
"properties": {
"id": {
"type": "Number",
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index e1aaa2cd6..c44191381 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -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
diff --git a/modules/invoiceIn/back/methods/invoice-in/clone.js b/modules/invoiceIn/back/methods/invoice-in/clone.js
new file mode 100644
index 000000000..1593a68b7
--- /dev/null
+++ b/modules/invoiceIn/back/methods/invoice-in/clone.js
@@ -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;
+ }
+ };
+};
diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js
new file mode 100644
index 000000000..09b7e6019
--- /dev/null
+++ b/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js
@@ -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;
+ }
+ });
+});
diff --git a/modules/invoiceIn/back/model-config.json b/modules/invoiceIn/back/model-config.json
index 467d6f7f9..f0745f53b 100644
--- a/modules/invoiceIn/back/model-config.json
+++ b/modules/invoiceIn/back/model-config.json
@@ -2,6 +2,9 @@
"InvoiceIn": {
"dataSource": "vn"
},
+ "InvoiceInTax": {
+ "dataSource": "vn"
+ },
"InvoiceInDueDay": {
"dataSource": "vn"
},
diff --git a/modules/invoiceIn/back/models/invoice-in-due-day.json b/modules/invoiceIn/back/models/invoice-in-due-day.json
index 6c27dcd6c..5a66ecd8b 100644
--- a/modules/invoiceIn/back/models/invoice-in-due-day.json
+++ b/modules/invoiceIn/back/models/invoice-in-due-day.json
@@ -24,6 +24,9 @@
"amount": {
"type": "number"
},
+ "foreignValue": {
+ "type": "number"
+ },
"created": {
"type": "date"
}
diff --git a/modules/invoiceIn/back/models/invoice-in-tax.json b/modules/invoiceIn/back/models/invoice-in-tax.json
new file mode 100644
index 000000000..abc137ecd
--- /dev/null
+++ b/modules/invoiceIn/back/models/invoice-in-tax.json
@@ -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"
+ }
+ }
+}
+
+
diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js
index 7c5b16358..7754890ca 100644
--- a/modules/invoiceIn/back/models/invoice-in.js
+++ b/modules/invoiceIn/back/models/invoice-in.js
@@ -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);
};
diff --git a/modules/invoiceIn/back/models/invoice-in.json b/modules/invoiceIn/back/models/invoice-in.json
index 468972523..9ec08c759 100644
--- a/modules/invoiceIn/back/models/invoice-in.json
+++ b/modules/invoiceIn/back/models/invoice-in.json
@@ -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"
},
diff --git a/modules/invoiceIn/front/descriptor/index.html b/modules/invoiceIn/front/descriptor/index.html
index 42a946913..6829a0daf 100644
--- a/modules/invoiceIn/front/descriptor/index.html
+++ b/modules/invoiceIn/front/descriptor/index.html
@@ -8,6 +8,13 @@
translate>
Delete Invoice
+
+ Clone Invoice
+
@@ -42,8 +49,16 @@
-
-
+
+
+
diff --git a/modules/invoiceIn/front/descriptor/index.js b/modules/invoiceIn/front/descriptor/index.js
index be507e0d4..a767f4b5c 100644
--- a/modules/invoiceIn/front/descriptor/index.js
+++ b/modules/invoiceIn/front/descriptor/index.js
@@ -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: [
diff --git a/modules/invoiceIn/front/locale/es.yml b/modules/invoiceIn/front/locale/es.yml
index ac432aba8..dee0d2bc1 100644
--- a/modules/invoiceIn/front/locale/es.yml
+++ b/modules/invoiceIn/front/locale/es.yml
@@ -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
\ No newline at end of file
+InvoiceIn deleted: Factura eliminada
+InvoiceIn cloned: Factura clonada
\ No newline at end of file
diff --git a/modules/invoiceOut/front/descriptor/locale/es.yml b/modules/invoiceOut/front/descriptor/locale/es.yml
index ae4396a01..ec9cd3310 100644
--- a/modules/invoiceOut/front/descriptor/locale/es.yml
+++ b/modules/invoiceOut/front/descriptor/locale/es.yml
@@ -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?