From a89d1e883a1f911568c861d8a3c7edb52943283c Mon Sep 17 00:00:00 2001 From: jorgep Date: Tue, 26 Dec 2023 14:36:58 +0100 Subject: [PATCH 1/8] create rectification: refs #4466 --- db/changes/240201/00-invoiceRectification.sql | 21 ++++++ .../back/methods/invoice-in/clone.js | 71 ++++++++++++++----- .../back/methods/invoice-in/rectification.js | 58 +++++++++++++++ modules/invoiceIn/back/model-config.json | 3 + .../back/models/invoice-in-correction.json | 41 +++++++++++ modules/invoiceIn/back/models/invoice-in.js | 1 + 6 files changed, 176 insertions(+), 19 deletions(-) create mode 100644 db/changes/240201/00-invoiceRectification.sql create mode 100644 modules/invoiceIn/back/methods/invoice-in/rectification.js create mode 100644 modules/invoiceIn/back/models/invoice-in-correction.json diff --git a/db/changes/240201/00-invoiceRectification.sql b/db/changes/240201/00-invoiceRectification.sql new file mode 100644 index 000000000..4648a7eb2 --- /dev/null +++ b/db/changes/240201/00-invoiceRectification.sql @@ -0,0 +1,21 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('InvoiceIn', 'rectification', 'WRITE', 'ALLOW', 'ROLE', 'employee'); + +CREATE TABLE `vn`.`invoiceInCorrection` ( + `correctingFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificativa', + `correctedFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificada', + `cplusRectificationTypeFk` int(10) UNSIGNED NOT NULL, + `siiTypeInvoiceOutFk` int(10) UNSIGNED NOT NULL, + `invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3, + PRIMARY KEY (`correctingFk`), + KEY `invoiceInCorrection_correctedFk` (`correctedFk`), + KEY `invoiceInCorrection_cplusRectificationTypeFk` (`cplusRectificationTypeFk`), + KEY `invoiceInCorrection_siiTypeInvoiceOut` (`siiTypeInvoiceOutFk`), + KEY `invoiceInCorrection_invoiceCorrectionTypeFk` (`invoiceCorrectionTypeFk`), + CONSTRAINT `invoiceInCorrection_correctedFk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_correctingFk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_siiTypeInvoiceOut` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_invoiceCorrectionTypeFk` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_cplusRectificationTypeFk` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE +); diff --git a/modules/invoiceIn/back/methods/invoice-in/clone.js b/modules/invoiceIn/back/methods/invoice-in/clone.js index 18df53a95..662ef52be 100644 --- a/modules/invoiceIn/back/methods/invoice-in/clone.js +++ b/modules/invoiceIn/back/methods/invoice-in/clone.js @@ -2,13 +2,17 @@ module.exports = Self => { Self.remoteMethodCtx('clone', { description: 'Clone the invoiceIn and as many invoiceInTax and invoiceInDueDay referencing it', accessType: 'WRITE', - accepts: { + accepts: [{ arg: 'id', type: 'number', required: true, description: 'The invoiceIn id', http: {source: 'path'} - }, + }, { + arg: 'isRectification', + type: 'boolean', + description: 'Clone quantities in negative and clone Intrastat' + }], returns: { type: 'object', root: true @@ -19,7 +23,7 @@ module.exports = Self => { } }); - Self.clone = async(ctx, id, options) => { + Self.clone = async(ctx, id, isRectification, options) => { const models = Self.app.models; let tx; const myOptions = {}; @@ -45,10 +49,22 @@ module.exports = Self => { 'isVatDeductible', 'withholdingSageFk', 'deductibleExpenseFk', - ] + ], + include: [ + { + relation: 'invoiceInTax', + }, + { + relation: 'invoiceInDueDay', + }, + { + relation: 'invoiceInIntrastat' + } + ], }, myOptions); - const sourceInvoiceInTax = await models.InvoiceInTax.find({where: {invoiceInFk: id}}, myOptions); - const sourceInvoiceInDueDay = await models.InvoiceInDueDay.find({where: {invoiceInFk: id}}, myOptions); + const invoiceInTax = sourceInvoiceIn.invoiceInTax(); + const invoiceInDueDay = sourceInvoiceIn.invoiceInDueDay(); + const invoiceInIntrastat = sourceInvoiceIn.invoiceInIntrastat(); const issued = new Date(sourceInvoiceIn.issued); issued.setMonth(issued.getMonth() + 1); @@ -68,30 +84,47 @@ module.exports = Self => { const promises = []; - for (let tax of sourceInvoiceInTax) { + for (let tax of invoiceInTax) { promises.push(models.InvoiceInTax.create({ invoiceInFk: clone.id, - taxableBase: tax.taxableBase, + taxableBase: isRectification ? -tax.taxableBase : tax.taxableBase, expenseFk: tax.expenseFk, - foreignValue: tax.foreignValue, + foreignValue: isRectification ? -tax.foreignValue : tax.foreignValue, taxTypeSageFk: tax.taxTypeSageFk, transactionTypeSageFk: tax.transactionTypeSageFk }, myOptions)); } - for (let dueDay of sourceInvoiceInDueDay) { - const dueDated = dueDay.dueDated; - dueDated.setMonth(dueDated.getMonth() + 1); + if (!isRectification) { + for (let dueDay of invoiceInDueDay) { + 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)); + promises.push(models.InvoiceInDueDay.create({ + invoiceInFk: clone.id, + dueDated: dueDated, + bankFk: dueDay.bankFk, + amount: dueDay.amount, + foreignValue: dueDated.foreignValue, + }, myOptions)); + } } + if (isRectification) { + console.log(invoiceInIntrastat); + for (let intrastat of invoiceInIntrastat) { + promises.push(models.InvoiceInIntrastat.create({ + invoiceInFk: clone.id, + net: -intrastat.net, + intrastatFk: intrastat.intrastatFk, + amount: -intrastat.amount, + stems: -intrastat.stems, + country: intrastat.countryFk, + dated: Date.vnNew(), + statisticalValue: intrastat.statisticalValue + }, myOptions)); + } + } await Promise.all(promises); if (tx) await tx.commit(); diff --git a/modules/invoiceIn/back/methods/invoice-in/rectification.js b/modules/invoiceIn/back/methods/invoice-in/rectification.js new file mode 100644 index 000000000..95cac6d16 --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/rectification.js @@ -0,0 +1,58 @@ +module.exports = Self => { + Self.remoteMethodCtx('rectification', { + description: 'Creates a rectificated invoice in', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'number' + }, { + arg: 'invoiceReason', + type: 'string', + }, { + arg: 'invoiceType', + type: 'string', + }, { + arg: 'invoiceClass', + type: 'string' + }], + returns: { + type: 'number', + root: true + }, + http: { + path: '/rectification', + verb: 'POST' + } + }); + + Self.rectification = async(ctx, id, invoiceReason, invoiceType, invoiceClass, options) => { + const models = Self.app.models; + const myOptions = {}; + let tx; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const clone = await Self.clone(ctx, id, true, myOptions); + await models.InvoiceInCorrection.create({ + correctingFk: id, + correctedFk: clone.id, + cplusRectificationTypeFk: invoiceReason, + siiTypeInvoiceOutFk: invoiceClass, + invoiceCorrectionTypeFk: invoiceType + }, myOptions); + + if (tx) await tx.commit(); + return clone.id; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/invoiceIn/back/model-config.json b/modules/invoiceIn/back/model-config.json index bd37b3bf1..4e0adf7a3 100644 --- a/modules/invoiceIn/back/model-config.json +++ b/modules/invoiceIn/back/model-config.json @@ -5,6 +5,9 @@ "InvoiceInConfig": { "dataSource": "vn" }, + "InvoiceInCorrection": { + "dataSource": "vn" + }, "InvoiceInDueDay": { "dataSource": "vn" }, diff --git a/modules/invoiceIn/back/models/invoice-in-correction.json b/modules/invoiceIn/back/models/invoice-in-correction.json new file mode 100644 index 000000000..742812f7d --- /dev/null +++ b/modules/invoiceIn/back/models/invoice-in-correction.json @@ -0,0 +1,41 @@ +{ + "name": "InvoiceInCorrection", + "base": "VnModel", + "options": { + "mysql": { + "table": "invoiceInCorrection" + } + }, + "properties": { + "correctingFk": { + "id": true, + "type": "number" + }, + "correctedFk": { + "type": "number" + } + }, + "relations": { + "invoiceIn": { + "type": "belongsTo", + "model": "InvoiceIn", + "foreignKey": "correctingFk" + }, + "cplusRectificationType": { + "type": "belongsTo", + "model": "CplusRectificationType", + "foreignKey": "cplusRectificationTypeFk" + }, + "invoiceCorrectionType": { + "type": "belongsTo", + "model": "InvoiceCorrectionType", + "foreignKey": "invoiceCorrectionTypeFk" + }, + "siiTypeInvoiceOutFk": { + "type": "belongsTo", + "model": "SiiTypeInvoiceOut", + "foreignKey": "siiTypeInvoiceOutFk" + } + + } +} \ No newline at end of file diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js index 82e0bf078..776ee720b 100644 --- a/modules/invoiceIn/back/models/invoice-in.js +++ b/modules/invoiceIn/back/models/invoice-in.js @@ -9,6 +9,7 @@ module.exports = Self => { require('../methods/invoice-in/invoiceInPdf')(Self); require('../methods/invoice-in/invoiceInEmail')(Self); require('../methods/invoice-in/getSerial')(Self); + require('../methods/invoice-in/rectification')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_ROW_IS_REFERENCED_2' && err.sqlMessage.includes('vehicleInvoiceIn')) return new UserError(`This invoice has a linked vehicle.`); From 567879d64e4986c32ab5d3a38f8d4d6b4c13fe5d Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 28 Dec 2023 15:55:11 +0100 Subject: [PATCH 2/8] corrective back created: refs #4466 --- db/changes/240201/00-invoiceCorrective.sql | 260 ++++++++++++++++++ db/changes/240201/00-invoiceRectification.sql | 21 -- .../back/methods/invoice-in/clone.js | 5 +- .../{rectification.js => corrective.js} | 20 +- .../back/models/invoice-in-correction.json | 5 +- modules/invoiceIn/back/models/invoice-in.js | 3 +- modules/invoiceIn/back/models/invoice-in.json | 5 + .../back/models/sii-type-invoice-out.json | 3 + 8 files changed, 284 insertions(+), 38 deletions(-) create mode 100644 db/changes/240201/00-invoiceCorrective.sql delete mode 100644 db/changes/240201/00-invoiceRectification.sql rename modules/invoiceIn/back/methods/invoice-in/{rectification.js => corrective.js} (72%) diff --git a/db/changes/240201/00-invoiceCorrective.sql b/db/changes/240201/00-invoiceCorrective.sql new file mode 100644 index 000000000..68dfefb0a --- /dev/null +++ b/db/changes/240201/00-invoiceCorrective.sql @@ -0,0 +1,260 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('InvoiceIn', 'corrective', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceInCorrection', '*', '*', 'ALLOW', 'ROLE', 'administrative'); + +CREATE TABLE `vn`.`invoiceInCorrection` ( + `correctingFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificativa', + `correctedFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificada', + `cplusRectificationTypeFk` int(10) UNSIGNED NOT NULL, + `siiTypeInvoiceOutFk` int(10) UNSIGNED NOT NULL, + `invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3, + PRIMARY KEY (`correctingFk`), + KEY `invoiceInCorrection_correctedFk` (`correctedFk`), + KEY `invoiceInCorrection_cplusRectificationTypeFk` (`cplusRectificationTypeFk`), + KEY `invoiceInCorrection_siiTypeInvoiceOut` (`siiTypeInvoiceOutFk`), + KEY `invoiceInCorrection_invoiceCorrectionTypeFk` (`invoiceCorrectionTypeFk`), + CONSTRAINT `invoiceInCorrection_correctedFk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_correctingFk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_siiTypeInvoiceOut` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_invoiceCorrectionTypeFk` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_cplusRectificationTypeFk` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE +); + +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `sage`.`invoiceIn_add`(vInvoiceInFk INT, vXDiarioFk INT) +BEGIN +/** + * Traslada la info de contabilidad relacionada con las facturas recibidas + * + * @vInvoiceInFk Factura recibida + * @vXDiarioFk Id tabla XDiario + */ + DECLARE vInvoiceInOriginalFk INT; + DECLARE vDone BOOL DEFAULT FALSE; + DECLARE vBase DOUBLE; + DECLARE vVat DOUBLE; + DECLARE vRate DOUBLE; + DECLARE vTransactionCode INT; + DECLARE vCounter INT DEFAULT 0; + DECLARE vTransactionCodeOld INT; + DECLARE vTaxCode INT; + DECLARE vTaxCodeOld INT; + DECLARE vOperationCode VARCHAR(1); + DECLARE vIsIntracommunity BOOL DEFAULT FALSE; + DECLARE vSerialDua VARCHAR(1) DEFAULT 'D'; + DECLARE vInvoiceTypeReceived VARCHAR(1); + DECLARE vInvoiceTypeInformative VARCHAR(1); + DECLARE vIsInformativeExportation BOOL DEFAULT FALSE; + + DECLARE vCursor CURSOR FOR + SELECT it.taxableBase, + CAST((( it.taxableBase / 100) * t.PorcentajeIva) AS DECIMAL (10,2)), + t.PorcentajeIva, + it.transactionTypeSageFk, + it.taxTypeSageFk, + tty.isIntracommunity, + tt.ClaveOperacionDefecto + FROM vn.invoiceIn i + JOIN vn.invoiceInTax it ON it.InvoiceInFk = i.id + JOIN TiposIva t ON t.CodigoIva = it.taxTypeSageFk + JOIN taxType tty ON tty.id = t.CodigoIva + JOIN TiposTransacciones tt ON tt.CodigoTransaccion = it.transactionTypeSageFk + LEFT JOIN vn.dua d ON d.id = vInvoiceInFk + WHERE i.id = vInvoiceInFk + AND d.id IS NULL; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + + DELETE FROM movContaIVA + WHERE id = vXDiarioFk; + + SELECT codeSage INTO vInvoiceTypeReceived + FROM invoiceType WHERE code ='received'; + + SELECT codeSage INTO vInvoiceTypeInformative + FROM invoiceType WHERE code ='informative'; + + INSERT INTO movContaIVA(id, LibreA1) + VALUES (vXDiarioFk, vInvoiceInFk); + + OPEN vCursor; + + l: LOOP + FETCH vCursor INTO vBase, + vVat, + vRate, + vTransactionCode, + vTaxCode, + vIsIntracommunity, + vOperationCode; + + IF vDone THEN + LEAVE l; + END IF; + + SET vTransactionCodeOld = vTransactionCode; + SET vTaxCodeOld = vTaxCode; + + IF vOperationCode IS NOT NULL THEN + UPDATE movContaIVA + SET ClaveOperacionFactura = vOperationCode + WHERE id = vXDiarioFk; + END IF; + + SET vCounter = vCounter + 1; + CASE vCounter + WHEN 1 THEN + UPDATE movContaIVA + SET BaseIva1 = vBase, + PorIva1 = vRate, + CuotaIva1 = vVat, + CodigoTransaccion1 = vTransactionCode, + CodigoIva1 = vTaxCode + WHERE id = vXDiarioFk; + + WHEN 2 THEN + UPDATE movContaIVA + SET BaseIva2 = vBase, + PorIva2 = vRate, + CuotaIva2 = vVat, + CodigoTransaccion2 = vTransactionCode, + CodigoIva2 = vTaxCode + WHERE id = vXDiarioFk; + WHEN 3 THEN + UPDATE movContaIVA + SET BaseIva3 = vBase, + PorIva3 = vRate, + CuotaIva3 = vVat, + CodigoTransaccion3 = vTransactionCode, + CodigoIva3 = vTaxCode + WHERE id = vXDiarioFk; + WHEN 4 THEN + UPDATE movContaIVA + SET BaseIva4 = vBase, + PorIva4 = vRate, + CuotaIva4 = vVat, + CodigoTransaccion4 = vTransactionCode, + CodigoIva4 = vTaxCode + WHERE id = vXDiarioFk; + ELSE + SELECT vXDiarioFk INTO vXDiarioFk; + END CASE; + + IF vIsIntracommunity THEN + UPDATE movContaIVA + SET Intracomunitaria = TRUE + WHERE id = vXDiarioFk; + END IF; + + SET vTransactionCodeOld = vTransactionCode; + SET vTaxCodeOld = vTaxCode; + + END LOOP; + + CLOSE vCursor; + + SELECT d.ASIEN AND x.ASIEN IS NULL INTO vIsInformativeExportation + FROM vn.dua d + LEFT JOIN vn.XDiario x ON x.ASIEN = d.ASIEN + AND x.SERIE = vSerialDua COLLATE utf8mb3_unicode_ci + WHERE d.ASIEN = ( + SELECT ASIEN + FROM vn.XDiario + WHERE id = vXDiarioFk) + LIMIT 1; + + UPDATE movContaIVA mci + JOIN tmp.invoiceIn ii ON ii.id = vInvoiceInFk + JOIN vn.XDiario x ON x.id = mci.id + LEFT JOIN tmp.invoiceDua id ON id.id = mci.id + JOIN vn.supplier s ON s.id = ii.supplierFk + JOIN Naciones n ON n.countryFk = s.countryFk + SET mci.CodigoDivisa = ii.currencyFk, + mci.Año = YEAR(ii.issued), + mci.Serie = ii.serial, + mci.Factura = ii.id, + mci.FechaFactura = ii.issued, + mci.ImporteFactura = IFNULL(mci.BaseIva1, 0) + IFNULL(mci.CuotaIva1, 0) + + IFNULL(mci.BaseIva2, 0) + IFNULL(mci.CuotaIva2, 0) + + IFNULL(mci.BaseIva3, 0) + IFNULL(mci.CuotaIva3, 0) + + IFNULL(mci.BaseIva4, 0) + IFNULL(mci.CuotaIva4, 0), + mci.TipoFactura = IF(id.id, + IF( ii.serial = vSerialDua COLLATE utf8mb3_unicode_ci, vInvoiceTypeReceived, vInvoiceTypeInformative), + IF(vIsInformativeExportation,vInvoiceTypeInformative, vInvoiceTypeReceived)), + mci.CodigoCuentaFactura = x.SUBCTA, + mci.CifDni = IF(LEFT(TRIM(s.nif), 2) = n.SiglaNacion, SUBSTRING(TRIM(s.nif), 3), s.nif), + mci.Nombre = s.name, + mci.SiglaNacion = n.SiglaNacion, + mci.EjercicioFactura = YEAR(ii.issued), + mci.FechaOperacion = ii.issued, + mci.MantenerAsiento = TRUE, + mci.SuFacturaNo = ii.supplierRef, + mci.IvaDeducible1 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva1, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.IvaDeducible2 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva2, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.IvaDeducible3 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva3, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.IvaDeducible4 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva4, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.FechaFacturaOriginal = x.FECHA_EX + WHERE mci.id = vXDiarioFk; + + -- RETENCIONES + UPDATE movContaIVA mci + JOIN vn.invoiceIn ii ON ii.id = vInvoiceInFk + JOIN vn.XDiario x ON x.id = mci.id + JOIN vn.supplier s ON s.id = supplierFk + JOIN vn.invoiceInTax iit ON iit.invoiceInFk = ii.id + JOIN vn.expense e ON e.id = iit.expenseFk + JOIN TiposRetencion t ON t.CodigoRetencion = ii.withholdingSageFk + LEFT JOIN tmp.invoiceDua id ON id.id = mci.id + JOIN (SELECT SUM(x2.BASEEURO) taxableBase, SUM(x2.EURODEBE) taxBase + FROM vn.XDiario x1 + JOIN vn.XDiario x2 ON x1.ASIEN = x2.ASIEN + WHERE x2.BASEEURO <> 0 + AND x1.id = vXDiarioFk + )sub + JOIN ClavesOperacion co ON co.Descripcion = 'Arrendamiento de locales de negocio' + SET mci.CodigoRetencion = t.CodigoRetencion, + mci.ClaveOperacionFactura = IF( t.Retencion = 'ARRENDAMIENTO Y SUBARRENDAMIENTO', co.ClaveOperacionFactura_, mci.ClaveOperacionFactura), + mci.BaseRetencion = IF (t.Retencion = 'ACTIVIDADES AGRICOLAS O GANADERAS', sub.taxableBase + sub.taxBase, sub.taxableBase), + mci.PorRetencion = t.PorcentajeRetencion, + mci.ImporteRetencion = iit.taxableBase * - 1 + WHERE mci.id = vXDiarioFk + AND e.name = 'Retenciones' + AND id.id IS NULL; + + -- SAGE + SELECT correctedFk INTO vInvoiceInOriginalFk + FROM vn.invoiceInCorrection + WHERE correctingFk = vInvoiceInFk; + + IF vInvoiceInOriginalFk THEN + + UPDATE movContaIVA mci + JOIN vn.invoiceInRefund iir ON iir.invoiceInRefundFk = vInvoiceInFk + JOIN (SELECT issued, + SUM(sub.taxableBase) taxableBase, + SUM(ROUND((sub.taxableBase * sub.PorcentajeIva) / 100 , 2)) vat + FROM(SELECT issued, + SUM(iit.taxableBase) taxableBase, + ti.PorcentajeIva + FROM vn.invoiceIn i + JOIN vn.invoiceInTax iit ON iit.invoiceInFk = i.id + JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk + WHERE i.id = vInvoiceInOriginalFk + GROUP BY ti.CodigoIva)sub + )invoiceInOriginal + JOIN ClavesOperacion co ON co.Descripcion = 'Factura rectificativa' + SET mci.TipoRectificativa = iir.refundCategoryFk, + mci.ClaseAbonoRectificativas = iir.refundType, + mci.FechaFacturaOriginal = invoiceInOriginal.issued, + mci.FechaOperacion = invoiceInOriginal.issued, + mci.BaseImponibleOriginal = invoiceInOriginal.taxableBase, + mci.CuotaIvaOriginal = invoiceInOriginal.vat, + mci.ClaveOperacionFactura = co.ClaveOperacionFactura_ + WHERE mci.id = vXDiarioFk; + + END IF; + + +END$$ +DELIMITER ; \ No newline at end of file diff --git a/db/changes/240201/00-invoiceRectification.sql b/db/changes/240201/00-invoiceRectification.sql deleted file mode 100644 index 4648a7eb2..000000000 --- a/db/changes/240201/00-invoiceRectification.sql +++ /dev/null @@ -1,21 +0,0 @@ -INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) - VALUES - ('InvoiceIn', 'rectification', 'WRITE', 'ALLOW', 'ROLE', 'employee'); - -CREATE TABLE `vn`.`invoiceInCorrection` ( - `correctingFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificativa', - `correctedFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificada', - `cplusRectificationTypeFk` int(10) UNSIGNED NOT NULL, - `siiTypeInvoiceOutFk` int(10) UNSIGNED NOT NULL, - `invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3, - PRIMARY KEY (`correctingFk`), - KEY `invoiceInCorrection_correctedFk` (`correctedFk`), - KEY `invoiceInCorrection_cplusRectificationTypeFk` (`cplusRectificationTypeFk`), - KEY `invoiceInCorrection_siiTypeInvoiceOut` (`siiTypeInvoiceOutFk`), - KEY `invoiceInCorrection_invoiceCorrectionTypeFk` (`invoiceCorrectionTypeFk`), - CONSTRAINT `invoiceInCorrection_correctedFk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `invoiceInCorrection_correctingFk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `invoiceInCorrection_siiTypeInvoiceOut` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, - CONSTRAINT `invoiceInCorrection_invoiceCorrectionTypeFk` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE, - CONSTRAINT `invoiceInCorrection_cplusRectificationTypeFk` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE -); diff --git a/modules/invoiceIn/back/methods/invoice-in/clone.js b/modules/invoiceIn/back/methods/invoice-in/clone.js index 662ef52be..ce1ec178d 100644 --- a/modules/invoiceIn/back/methods/invoice-in/clone.js +++ b/modules/invoiceIn/back/methods/invoice-in/clone.js @@ -68,7 +68,9 @@ module.exports = Self => { const issued = new Date(sourceInvoiceIn.issued); issued.setMonth(issued.getMonth() + 1); - const clonedRef = sourceInvoiceIn.supplierRef + '(2)'; + const totalCorrections = await models.InvoiceInCorrection.count({correctedFk: id}, myOptions); + + const clonedRef = sourceInvoiceIn.supplierRef + `(${totalCorrections + 2})`; const clone = await models.InvoiceIn.create({ serial: sourceInvoiceIn.serial, @@ -111,7 +113,6 @@ module.exports = Self => { } if (isRectification) { - console.log(invoiceInIntrastat); for (let intrastat of invoiceInIntrastat) { promises.push(models.InvoiceInIntrastat.create({ invoiceInFk: clone.id, diff --git a/modules/invoiceIn/back/methods/invoice-in/rectification.js b/modules/invoiceIn/back/methods/invoice-in/corrective.js similarity index 72% rename from modules/invoiceIn/back/methods/invoice-in/rectification.js rename to modules/invoiceIn/back/methods/invoice-in/corrective.js index 95cac6d16..05f632bcd 100644 --- a/modules/invoiceIn/back/methods/invoice-in/rectification.js +++ b/modules/invoiceIn/back/methods/invoice-in/corrective.js @@ -1,5 +1,5 @@ module.exports = Self => { - Self.remoteMethodCtx('rectification', { + Self.remoteMethodCtx('corrective', { description: 'Creates a rectificated invoice in', accessType: 'WRITE', accepts: [{ @@ -7,25 +7,25 @@ module.exports = Self => { type: 'number' }, { arg: 'invoiceReason', - type: 'string', + type: 'number', }, { arg: 'invoiceType', - type: 'string', + type: 'number', }, { arg: 'invoiceClass', - type: 'string' + type: 'number' }], returns: { type: 'number', root: true }, http: { - path: '/rectification', + path: '/corrective', verb: 'POST' } }); - Self.rectification = async(ctx, id, invoiceReason, invoiceType, invoiceClass, options) => { + Self.corrective = async(ctx, id, invoiceReason, invoiceType, invoiceClass, options) => { const models = Self.app.models; const myOptions = {}; let tx; @@ -41,11 +41,11 @@ module.exports = Self => { try { const clone = await Self.clone(ctx, id, true, myOptions); await models.InvoiceInCorrection.create({ - correctingFk: id, - correctedFk: clone.id, - cplusRectificationTypeFk: invoiceReason, + correctingFk: clone.id, + correctedFk: id, + cplusRectificationTypeFk: invoiceType, siiTypeInvoiceOutFk: invoiceClass, - invoiceCorrectionTypeFk: invoiceType + invoiceCorrectionTypeFk: invoiceReason }, myOptions); if (tx) await tx.commit(); diff --git a/modules/invoiceIn/back/models/invoice-in-correction.json b/modules/invoiceIn/back/models/invoice-in-correction.json index 742812f7d..e94bb9ed8 100644 --- a/modules/invoiceIn/back/models/invoice-in-correction.json +++ b/modules/invoiceIn/back/models/invoice-in-correction.json @@ -10,16 +10,13 @@ "correctingFk": { "id": true, "type": "number" - }, - "correctedFk": { - "type": "number" } }, "relations": { "invoiceIn": { "type": "belongsTo", "model": "InvoiceIn", - "foreignKey": "correctingFk" + "foreignKey": "correctedFk" }, "cplusRectificationType": { "type": "belongsTo", diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js index 776ee720b..af5efbcdf 100644 --- a/modules/invoiceIn/back/models/invoice-in.js +++ b/modules/invoiceIn/back/models/invoice-in.js @@ -9,7 +9,8 @@ module.exports = Self => { require('../methods/invoice-in/invoiceInPdf')(Self); require('../methods/invoice-in/invoiceInEmail')(Self); require('../methods/invoice-in/getSerial')(Self); - require('../methods/invoice-in/rectification')(Self); + require('../methods/invoice-in/corrective')(Self); + Self.rewriteDbError(function(err) { if (err.code === 'ER_ROW_IS_REFERENCED_2' && err.sqlMessage.includes('vehicleInvoiceIn')) return new UserError(`This invoice has a linked vehicle.`); diff --git a/modules/invoiceIn/back/models/invoice-in.json b/modules/invoiceIn/back/models/invoice-in.json index 5be55c851..f00f50ecd 100644 --- a/modules/invoiceIn/back/models/invoice-in.json +++ b/modules/invoiceIn/back/models/invoice-in.json @@ -100,6 +100,11 @@ "type": "belongsTo", "model": "Dms", "foreignKey": "dmsFk" + }, + "invoiceInCorrection": { + "type": "hasOne", + "model": "InvoiceInCorrection", + "foreignKey": "correctedFk" } } } diff --git a/modules/invoiceOut/back/models/sii-type-invoice-out.json b/modules/invoiceOut/back/models/sii-type-invoice-out.json index 17b312617..24e90a8d5 100644 --- a/modules/invoiceOut/back/models/sii-type-invoice-out.json +++ b/modules/invoiceOut/back/models/sii-type-invoice-out.json @@ -14,6 +14,9 @@ }, "description": { "type": "string" + }, + "code": { + "type": "string" } } } From e33861a80d1da65ba34ef8dac31f77eb519411e0 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 26 Jan 2024 12:15:53 +0100 Subject: [PATCH 3/8] fix: refs #6703 filter --- .../00-invoiceCorrective.sql | 0 .../back/methods/invoice-in/filter.js | 18 +++++++++++++++++- .../back/models/invoice-in-correction.json | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) rename db/changes/{240201 => 240601}/00-invoiceCorrective.sql (100%) diff --git a/db/changes/240201/00-invoiceCorrective.sql b/db/changes/240601/00-invoiceCorrective.sql similarity index 100% rename from db/changes/240201/00-invoiceCorrective.sql rename to db/changes/240601/00-invoiceCorrective.sql diff --git a/modules/invoiceIn/back/methods/invoice-in/filter.js b/modules/invoiceIn/back/methods/invoice-in/filter.js index e9d3da43c..9a4495372 100644 --- a/modules/invoiceIn/back/methods/invoice-in/filter.js +++ b/modules/invoiceIn/back/methods/invoice-in/filter.js @@ -1,4 +1,3 @@ - const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const buildFilter = require('vn-loopback/util/filter').buildFilter; const mergeFilters = require('vn-loopback/util/filter').mergeFilters; @@ -80,6 +79,11 @@ module.exports = Self => { type: 'boolean', description: 'Whether the invoice is booked or not', }, + { + arg: 'correctedFk', + type: 'number', + description: 'The corrected invoice', + } ], returns: { type: ['object'], @@ -93,6 +97,7 @@ module.exports = Self => { Self.filter = async(ctx, filter, options) => { const conn = Self.dataSource.connector; + const models = Self.app.models; const args = ctx.args; const myOptions = {}; @@ -105,6 +110,14 @@ module.exports = Self => { dateTo.setHours(23, 59, 0, 0); } + let correctings; + if (args.correctedFk) { + correctings = await models.InvoiceInCorrection.find({ + fields: ['correctingFk'], + where: {correctedFk: args.correctedFk} + }); + } + const where = buildFilter(ctx.args, (param, value) => { switch (param) { case 'search': @@ -128,6 +141,8 @@ module.exports = Self => { return {[`ii.${param}`]: value}; case 'awbCode': return {'sub.code': value}; + case 'correctedFk': + return {'ii.id': {inq: correctings.map(x => x.correctingFk)}}; } }); @@ -186,6 +201,7 @@ module.exports = Self => { const itemsIndex = stmts.push(stmt) - 1; const sql = ParameterizedSQL.join(stmts, ';'); + console.log(sql); const result = await conn.executeStmt(sql, myOptions); return itemsIndex === 0 ? result : result[itemsIndex]; diff --git a/modules/invoiceIn/back/models/invoice-in-correction.json b/modules/invoiceIn/back/models/invoice-in-correction.json index e94bb9ed8..52e16d420 100644 --- a/modules/invoiceIn/back/models/invoice-in-correction.json +++ b/modules/invoiceIn/back/models/invoice-in-correction.json @@ -28,7 +28,7 @@ "model": "InvoiceCorrectionType", "foreignKey": "invoiceCorrectionTypeFk" }, - "siiTypeInvoiceOutFk": { + "siiTypeInvoiceOut": { "type": "belongsTo", "model": "SiiTypeInvoiceOut", "foreignKey": "siiTypeInvoiceOutFk" From ab3fdf4fe4078d68f05e44eb92cb54b33a672a80 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 26 Jan 2024 12:33:04 +0100 Subject: [PATCH 4/8] fix: refs #6703 remove console log --- modules/invoiceIn/back/methods/invoice-in/filter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/invoiceIn/back/methods/invoice-in/filter.js b/modules/invoiceIn/back/methods/invoice-in/filter.js index 9a4495372..0d3b5f14a 100644 --- a/modules/invoiceIn/back/methods/invoice-in/filter.js +++ b/modules/invoiceIn/back/methods/invoice-in/filter.js @@ -201,7 +201,6 @@ module.exports = Self => { const itemsIndex = stmts.push(stmt) - 1; const sql = ParameterizedSQL.join(stmts, ';'); - console.log(sql); const result = await conn.executeStmt(sql, myOptions); return itemsIndex === 0 ? result : result[itemsIndex]; From 743f317b20879cdde0627a377300ed4b265d3360 Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 2 Feb 2024 14:02:16 +0100 Subject: [PATCH 5/8] fix: refs #4466 test & sql --- db/routines/vn/procedures/invoiceIn_add.sql | 237 ++++++++++++++++++ .../00-createAclInvoiceIn.sql | 4 + .../01-createInvoiceInCorrection.sql | 17 ++ .../methods/invoice-in/specs/clone.spec.js | 25 +- 4 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 db/routines/vn/procedures/invoiceIn_add.sql create mode 100644 db/versions/10867-yellowAsparagus/00-createAclInvoiceIn.sql create mode 100644 db/versions/10867-yellowAsparagus/01-createInvoiceInCorrection.sql diff --git a/db/routines/vn/procedures/invoiceIn_add.sql b/db/routines/vn/procedures/invoiceIn_add.sql new file mode 100644 index 000000000..a0979024b --- /dev/null +++ b/db/routines/vn/procedures/invoiceIn_add.sql @@ -0,0 +1,237 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `sage`.`invoiceIn_add`(vInvoiceInFk INT, vXDiarioFk INT) +BEGIN +/** + * Traslada la info de contabilidad relacionada con las facturas recibidas + * + * @vInvoiceInFk Factura recibida + * @vXDiarioFk Id tabla XDiario + */ + DECLARE vInvoiceInOriginalFk INT; + DECLARE vDone BOOL DEFAULT FALSE; + DECLARE vBase DOUBLE; + DECLARE vVat DOUBLE; + DECLARE vRate DOUBLE; + DECLARE vTransactionCode INT; + DECLARE vCounter INT DEFAULT 0; + DECLARE vTransactionCodeOld INT; + DECLARE vTaxCode INT; + DECLARE vTaxCodeOld INT; + DECLARE vOperationCode VARCHAR(1); + DECLARE vIsIntracommunity BOOL DEFAULT FALSE; + DECLARE vSerialDua VARCHAR(1) DEFAULT 'D'; + DECLARE vInvoiceTypeReceived VARCHAR(1); + DECLARE vInvoiceTypeInformative VARCHAR(1); + DECLARE vIsInformativeExportation BOOL DEFAULT FALSE; + + DECLARE vCursor CURSOR FOR + SELECT it.taxableBase, + CAST((( it.taxableBase / 100) * t.PorcentajeIva) AS DECIMAL (10,2)), + t.PorcentajeIva, + it.transactionTypeSageFk, + it.taxTypeSageFk, + tty.isIntracommunity, + tt.ClaveOperacionDefecto + FROM vn.invoiceIn i + JOIN vn.invoiceInTax it ON it.InvoiceInFk = i.id + JOIN TiposIva t ON t.CodigoIva = it.taxTypeSageFk + JOIN taxType tty ON tty.id = t.CodigoIva + JOIN TiposTransacciones tt ON tt.CodigoTransaccion = it.transactionTypeSageFk + LEFT JOIN vn.dua d ON d.id = vInvoiceInFk + WHERE i.id = vInvoiceInFk + AND d.id IS NULL; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + + DELETE FROM movContaIVA + WHERE id = vXDiarioFk; + + SELECT codeSage INTO vInvoiceTypeReceived + FROM invoiceType WHERE code ='received'; + + SELECT codeSage INTO vInvoiceTypeInformative + FROM invoiceType WHERE code ='informative'; + + INSERT INTO movContaIVA(id, LibreA1) + VALUES (vXDiarioFk, vInvoiceInFk); + + OPEN vCursor; + + l: LOOP + FETCH vCursor INTO vBase, + vVat, + vRate, + vTransactionCode, + vTaxCode, + vIsIntracommunity, + vOperationCode; + + IF vDone THEN + LEAVE l; + END IF; + + SET vTransactionCodeOld = vTransactionCode; + SET vTaxCodeOld = vTaxCode; + + IF vOperationCode IS NOT NULL THEN + UPDATE movContaIVA + SET ClaveOperacionFactura = vOperationCode + WHERE id = vXDiarioFk; + END IF; + + SET vCounter = vCounter + 1; + CASE vCounter + WHEN 1 THEN + UPDATE movContaIVA + SET BaseIva1 = vBase, + PorIva1 = vRate, + CuotaIva1 = vVat, + CodigoTransaccion1 = vTransactionCode, + CodigoIva1 = vTaxCode + WHERE id = vXDiarioFk; + + WHEN 2 THEN + UPDATE movContaIVA + SET BaseIva2 = vBase, + PorIva2 = vRate, + CuotaIva2 = vVat, + CodigoTransaccion2 = vTransactionCode, + CodigoIva2 = vTaxCode + WHERE id = vXDiarioFk; + WHEN 3 THEN + UPDATE movContaIVA + SET BaseIva3 = vBase, + PorIva3 = vRate, + CuotaIva3 = vVat, + CodigoTransaccion3 = vTransactionCode, + CodigoIva3 = vTaxCode + WHERE id = vXDiarioFk; + WHEN 4 THEN + UPDATE movContaIVA + SET BaseIva4 = vBase, + PorIva4 = vRate, + CuotaIva4 = vVat, + CodigoTransaccion4 = vTransactionCode, + CodigoIva4 = vTaxCode + WHERE id = vXDiarioFk; + ELSE + SELECT vXDiarioFk INTO vXDiarioFk; + END CASE; + + IF vIsIntracommunity THEN + UPDATE movContaIVA + SET Intracomunitaria = TRUE + WHERE id = vXDiarioFk; + END IF; + + SET vTransactionCodeOld = vTransactionCode; + SET vTaxCodeOld = vTaxCode; + + END LOOP; + + CLOSE vCursor; + + SELECT d.ASIEN AND x.ASIEN IS NULL INTO vIsInformativeExportation + FROM vn.dua d + LEFT JOIN vn.XDiario x ON x.ASIEN = d.ASIEN + AND x.SERIE = vSerialDua COLLATE utf8mb3_unicode_ci + WHERE d.ASIEN = ( + SELECT ASIEN + FROM vn.XDiario + WHERE id = vXDiarioFk) + LIMIT 1; + + UPDATE movContaIVA mci + JOIN tmp.invoiceIn ii ON ii.id = vInvoiceInFk + JOIN vn.XDiario x ON x.id = mci.id + LEFT JOIN tmp.invoiceDua id ON id.id = mci.id + JOIN vn.supplier s ON s.id = ii.supplierFk + JOIN Naciones n ON n.countryFk = s.countryFk + SET mci.CodigoDivisa = ii.currencyFk, + mci.Año = YEAR(ii.issued), + mci.Serie = ii.serial, + mci.Factura = ii.id, + mci.FechaFactura = ii.issued, + mci.ImporteFactura = IFNULL(mci.BaseIva1, 0) + IFNULL(mci.CuotaIva1, 0) + + IFNULL(mci.BaseIva2, 0) + IFNULL(mci.CuotaIva2, 0) + + IFNULL(mci.BaseIva3, 0) + IFNULL(mci.CuotaIva3, 0) + + IFNULL(mci.BaseIva4, 0) + IFNULL(mci.CuotaIva4, 0), + mci.TipoFactura = IF(id.id, + IF( ii.serial = vSerialDua COLLATE utf8mb3_unicode_ci, vInvoiceTypeReceived, vInvoiceTypeInformative), + IF(vIsInformativeExportation,vInvoiceTypeInformative, vInvoiceTypeReceived)), + mci.CodigoCuentaFactura = x.SUBCTA, + mci.CifDni = IF(LEFT(TRIM(s.nif), 2) = n.SiglaNacion, SUBSTRING(TRIM(s.nif), 3), s.nif), + mci.Nombre = s.name, + mci.SiglaNacion = n.SiglaNacion, + mci.EjercicioFactura = YEAR(ii.issued), + mci.FechaOperacion = ii.issued, + mci.MantenerAsiento = TRUE, + mci.SuFacturaNo = ii.supplierRef, + mci.IvaDeducible1 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva1, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.IvaDeducible2 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva2, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.IvaDeducible3 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva3, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.IvaDeducible4 = IF(id.id, FALSE, IF(IFNULL(mci.BaseIva4, FALSE) = FALSE, FALSE, ii.isVatDeductible)), + mci.FechaFacturaOriginal = x.FECHA_EX + WHERE mci.id = vXDiarioFk; + + -- RETENCIONES + UPDATE movContaIVA mci + JOIN vn.invoiceIn ii ON ii.id = vInvoiceInFk + JOIN vn.XDiario x ON x.id = mci.id + JOIN vn.supplier s ON s.id = supplierFk + JOIN vn.invoiceInTax iit ON iit.invoiceInFk = ii.id + JOIN vn.expense e ON e.id = iit.expenseFk + JOIN TiposRetencion t ON t.CodigoRetencion = ii.withholdingSageFk + LEFT JOIN tmp.invoiceDua id ON id.id = mci.id + JOIN (SELECT SUM(x2.BASEEURO) taxableBase, SUM(x2.EURODEBE) taxBase + FROM vn.XDiario x1 + JOIN vn.XDiario x2 ON x1.ASIEN = x2.ASIEN + WHERE x2.BASEEURO <> 0 + AND x1.id = vXDiarioFk + )sub + JOIN ClavesOperacion co ON co.Descripcion = 'Arrendamiento de locales de negocio' + SET mci.CodigoRetencion = t.CodigoRetencion, + mci.ClaveOperacionFactura = IF( t.Retencion = 'ARRENDAMIENTO Y SUBARRENDAMIENTO', co.ClaveOperacionFactura_, mci.ClaveOperacionFactura), + mci.BaseRetencion = IF (t.Retencion = 'ACTIVIDADES AGRICOLAS O GANADERAS', sub.taxableBase + sub.taxBase, sub.taxableBase), + mci.PorRetencion = t.PorcentajeRetencion, + mci.ImporteRetencion = iit.taxableBase * - 1 + WHERE mci.id = vXDiarioFk + AND e.name = 'Retenciones' + AND id.id IS NULL; + + -- SAGE + SELECT correctedFk INTO vInvoiceInOriginalFk + FROM vn.invoiceInCorrection + WHERE correctingFk = vInvoiceInFk; + + IF vInvoiceInOriginalFk THEN + + UPDATE movContaIVA mci + JOIN vn.invoiceInRefund iir ON iir.invoiceInRefundFk = vInvoiceInFk + JOIN (SELECT issued, + SUM(sub.taxableBase) taxableBase, + SUM(ROUND((sub.taxableBase * sub.PorcentajeIva) / 100 , 2)) vat + FROM(SELECT issued, + SUM(iit.taxableBase) taxableBase, + ti.PorcentajeIva + FROM vn.invoiceIn i + JOIN vn.invoiceInTax iit ON iit.invoiceInFk = i.id + JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk + WHERE i.id = vInvoiceInOriginalFk + GROUP BY ti.CodigoIva)sub + )invoiceInOriginal + JOIN ClavesOperacion co ON co.Descripcion = 'Factura rectificativa' + SET mci.TipoRectificativa = iir.refundCategoryFk, + mci.ClaseAbonoRectificativas = iir.refundType, + mci.FechaFacturaOriginal = invoiceInOriginal.issued, + mci.FechaOperacion = invoiceInOriginal.issued, + mci.BaseImponibleOriginal = invoiceInOriginal.taxableBase, + mci.CuotaIvaOriginal = invoiceInOriginal.vat, + mci.ClaveOperacionFactura = co.ClaveOperacionFactura_ + WHERE mci.id = vXDiarioFk; + + END IF; + + +END$$ +DELIMITER ; \ No newline at end of file diff --git a/db/versions/10867-yellowAsparagus/00-createAclInvoiceIn.sql b/db/versions/10867-yellowAsparagus/00-createAclInvoiceIn.sql new file mode 100644 index 000000000..927903d47 --- /dev/null +++ b/db/versions/10867-yellowAsparagus/00-createAclInvoiceIn.sql @@ -0,0 +1,4 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('InvoiceIn', 'corrective', 'WRITE', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceInCorrection', '*', '*', 'ALLOW', 'ROLE', 'administrative'); \ No newline at end of file diff --git a/db/versions/10867-yellowAsparagus/01-createInvoiceInCorrection.sql b/db/versions/10867-yellowAsparagus/01-createInvoiceInCorrection.sql new file mode 100644 index 000000000..0d084da9e --- /dev/null +++ b/db/versions/10867-yellowAsparagus/01-createInvoiceInCorrection.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS `vn`.`invoiceInCorrection` ( + `correctingFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificativa', + `correctedFk` mediumint(8) UNSIGNED NOT NULL COMMENT 'Factura rectificada', + `cplusRectificationTypeFk` int(10) UNSIGNED NOT NULL, + `siiTypeInvoiceOutFk` int(10) UNSIGNED NOT NULL, + `invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3, + PRIMARY KEY (`correctingFk`), + KEY `invoiceInCorrection_correctedFk` (`correctedFk`), + KEY `invoiceInCorrection_cplusRectificationTypeFk` (`cplusRectificationTypeFk`), + KEY `invoiceInCorrection_siiTypeInvoiceOut` (`siiTypeInvoiceOutFk`), + KEY `invoiceInCorrection_invoiceCorrectionTypeFk` (`invoiceCorrectionTypeFk`), + CONSTRAINT `invoiceInCorrection_correctedFk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_correctingFk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceIn` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_siiTypeInvoiceOut` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_invoiceCorrectionTypeFk` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceInCorrection_cplusRectificationTypeFk` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE +); \ No newline at end of file diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js index 42ebe52b3..b6c0fb160 100644 --- a/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js +++ b/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js @@ -30,17 +30,26 @@ describe('invoiceIn clone()', () => { const options = {transaction: tx}; try { - const clone = await models.InvoiceIn.clone(ctx, 1, options); + const clone = await models.InvoiceIn.clone(ctx, 1, false, options); expect(clone.supplierRef).toEqual('1234(2)'); + const invoiceIn = await models.InvoiceIn.findOne({ + include: [ + { + relation: 'invoiceInTax', + }, + { + relation: 'invoiceInDueDay', + } + ], where: { + id: clone.id + } + }, options); + const invoiceInTax = invoiceIn.invoiceInTax(); + const invoiceInDueDay = invoiceIn.invoiceInDueDay(); - const invoiceInTaxes = await models.InvoiceInTax.find({where: {invoiceInFk: clone.id}}, options); - - expect(invoiceInTaxes.length).toEqual(2); - - const invoiceInDueDays = await models.InvoiceInDueDay.find({where: {invoiceInFk: clone.id}}, options); - - expect(invoiceInDueDays.length).toEqual(2); + expect(invoiceInTax.length).toEqual(2); + expect(invoiceInDueDay.length).toEqual(2); await tx.rollback(); } catch (e) { From 2a6dc0c478104cc8cafccd7a9e0c2dd32a3f027c Mon Sep 17 00:00:00 2001 From: jorgep Date: Fri, 2 Feb 2024 15:56:05 +0100 Subject: [PATCH 6/8] fix: refs #4466 spaces --- db/routines/vn/procedures/invoiceIn_add.sql | 59 ++++++++++----------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/db/routines/vn/procedures/invoiceIn_add.sql b/db/routines/vn/procedures/invoiceIn_add.sql index a0979024b..0898d6810 100644 --- a/db/routines/vn/procedures/invoiceIn_add.sql +++ b/db/routines/vn/procedures/invoiceIn_add.sql @@ -198,40 +198,37 @@ BEGIN WHERE mci.id = vXDiarioFk AND e.name = 'Retenciones' AND id.id IS NULL; - - -- SAGE - SELECT correctedFk INTO vInvoiceInOriginalFk - FROM vn.invoiceInCorrection - WHERE correctingFk = vInvoiceInFk; - - IF vInvoiceInOriginalFk THEN - UPDATE movContaIVA mci - JOIN vn.invoiceInRefund iir ON iir.invoiceInRefundFk = vInvoiceInFk - JOIN (SELECT issued, - SUM(sub.taxableBase) taxableBase, - SUM(ROUND((sub.taxableBase * sub.PorcentajeIva) / 100 , 2)) vat - FROM(SELECT issued, - SUM(iit.taxableBase) taxableBase, - ti.PorcentajeIva - FROM vn.invoiceIn i - JOIN vn.invoiceInTax iit ON iit.invoiceInFk = i.id - JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk - WHERE i.id = vInvoiceInOriginalFk - GROUP BY ti.CodigoIva)sub - )invoiceInOriginal - JOIN ClavesOperacion co ON co.Descripcion = 'Factura rectificativa' - SET mci.TipoRectificativa = iir.refundCategoryFk, - mci.ClaseAbonoRectificativas = iir.refundType, - mci.FechaFacturaOriginal = invoiceInOriginal.issued, - mci.FechaOperacion = invoiceInOriginal.issued, - mci.BaseImponibleOriginal = invoiceInOriginal.taxableBase, - mci.CuotaIvaOriginal = invoiceInOriginal.vat, - mci.ClaveOperacionFactura = co.ClaveOperacionFactura_ - WHERE mci.id = vXDiarioFk; + SELECT correctedFk INTO vInvoiceInOriginalFk + FROM vn.invoiceInCorrection + WHERE correctingFk = vInvoiceInFk; - END IF; + IF vInvoiceInOriginalFk THEN + UPDATE movContaIVA mci + JOIN vn.invoiceInRefund iir ON iir.invoiceInRefundFk = vInvoiceInFk + JOIN (SELECT issued, + SUM(sub.taxableBase) taxableBase, + SUM(ROUND((sub.taxableBase * sub.PorcentajeIva) / 100 , 2)) vat + FROM(SELECT issued, + SUM(iit.taxableBase) taxableBase, + ti.PorcentajeIva + FROM vn.invoiceIn i + JOIN vn.invoiceInTax iit ON iit.invoiceInFk = i.id + JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk + WHERE i.id = vInvoiceInOriginalFk + GROUP BY ti.CodigoIva)sub + )invoiceInOriginal + JOIN ClavesOperacion co ON co.Descripcion = 'Factura rectificativa' + SET mci.TipoRectificativa = iir.refundCategoryFk, + mci.ClaseAbonoRectificativas = iir.refundType, + mci.FechaFacturaOriginal = invoiceInOriginal.issued, + mci.FechaOperacion = invoiceInOriginal.issued, + mci.BaseImponibleOriginal = invoiceInOriginal.taxableBase, + mci.CuotaIvaOriginal = invoiceInOriginal.vat, + mci.ClaveOperacionFactura = co.ClaveOperacionFactura_ + WHERE mci.id = vXDiarioFk; + END IF; END$$ DELIMITER ; \ No newline at end of file From 05478b43f25e119caf731f9473609e8a5127cbe5 Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 5 Feb 2024 09:30:29 +0100 Subject: [PATCH 7/8] fix: refs #4466 test back --- modules/invoiceIn/back/methods/invoice-in/clone.js | 4 +--- .../ticket/back/methods/sale/specs/clone.spec.js | 14 +++----------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/modules/invoiceIn/back/methods/invoice-in/clone.js b/modules/invoiceIn/back/methods/invoice-in/clone.js index ce1ec178d..88ea0ddcd 100644 --- a/modules/invoiceIn/back/methods/invoice-in/clone.js +++ b/modules/invoiceIn/back/methods/invoice-in/clone.js @@ -110,9 +110,7 @@ module.exports = Self => { foreignValue: dueDated.foreignValue, }, myOptions)); } - } - - if (isRectification) { + } else { for (let intrastat of invoiceInIntrastat) { promises.push(models.InvoiceInIntrastat.create({ invoiceInFk: clone.id, diff --git a/modules/ticket/back/methods/sale/specs/clone.spec.js b/modules/ticket/back/methods/sale/specs/clone.spec.js index f52f7ba38..e2220c028 100644 --- a/modules/ticket/back/methods/sale/specs/clone.spec.js +++ b/modules/ticket/back/methods/sale/specs/clone.spec.js @@ -70,19 +70,11 @@ describe('Ticket cloning - clone function', () => { it('should create a ticket without sales', async() => { const servicesIds = [4]; - const tx = await models.Sale.beginTransaction({}); - const options = {transaction: tx}; - try { - const tickets = await models.Sale.clone(ctx, null, servicesIds, false, options); - const refundedTicket = await getTicketRefund(tickets[0].id, options); - expect(refundedTicket).toBeDefined(); + const tickets = await models.Sale.clone(ctx, null, servicesIds, false, false, options); + const refundedTicket = await getTicketRefund(tickets[0].id, options); - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + expect(refundedTicket).toBeDefined(); }); }); From a0c5b1fff6f27d9be04370c085a3395753138132 Mon Sep 17 00:00:00 2001 From: jorgep Date: Mon, 5 Feb 2024 11:59:51 +0100 Subject: [PATCH 8/8] feat: refs # test corrective & clone --- .../methods/invoice-in/specs/clone.spec.js | 104 ++++++++++-------- .../invoice-in/specs/corrective.spec.js | 49 +++++++++ 2 files changed, 109 insertions(+), 44 deletions(-) create mode 100644 modules/invoiceIn/back/methods/invoice-in/specs/corrective.spec.js diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js index b6c0fb160..436306aab 100644 --- a/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js +++ b/modules/invoiceIn/back/methods/invoice-in/specs/clone.spec.js @@ -2,59 +2,75 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); describe('invoiceIn clone()', () => { - beforeAll(async() => { - const activeCtx = { - accessToken: {userId: 9}, - http: { - req: { - headers: {origin: 'http://localhost'} - } - } + let ctx; + let options; + let tx; + + beforeEach(async() => { + ctx = { + req: { + accessToken: {userId: 1}, + headers: {origin: 'http://localhost'} + }, + args: {} }; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ - active: activeCtx + active: ctx.req }); + + options = {transaction: tx}; + tx = await models.Sale.beginTransaction({}); + options.transaction = tx; + }); + + afterEach(async() => { + await tx.rollback(); }); 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: { + const clone = await models.InvoiceIn.clone(ctx, 1, false, options); - 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, false, options); - - expect(clone.supplierRef).toEqual('1234(2)'); - const invoiceIn = await models.InvoiceIn.findOne({ - include: [ - { - relation: 'invoiceInTax', - }, - { - relation: 'invoiceInDueDay', - } - ], where: { - id: clone.id + expect(clone.supplierRef).toEqual('1234(2)'); + const invoiceIn = await models.InvoiceIn.findOne({ + include: [ + { + relation: 'invoiceInTax', + }, + { + relation: 'invoiceInDueDay', } - }, options); - const invoiceInTax = invoiceIn.invoiceInTax(); - const invoiceInDueDay = invoiceIn.invoiceInDueDay(); + ], where: { + id: clone.id + } + }, options); + const invoiceInTax = invoiceIn.invoiceInTax(); + const invoiceInDueDay = invoiceIn.invoiceInDueDay(); - expect(invoiceInTax.length).toEqual(2); - expect(invoiceInDueDay.length).toEqual(2); + expect(invoiceInTax.length).toEqual(2); + expect(invoiceInDueDay.length).toEqual(2); + }); - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + it('should return the cloned invoiceIn and also clone invoiceInIntrastat and invoiceInTaxes if it is rectificative', async() => { + const clone = await models.InvoiceIn.clone(ctx, 1, true, options); + + expect(clone.supplierRef).toEqual('1234(2)'); + const invoiceIn = await models.InvoiceIn.findOne({ + include: [ + { + relation: 'invoiceInTax', + }, + { + relation: 'invoiceInIntrastat', + } + ], where: { + id: clone.id + } + }, options); + const invoiceInTax = invoiceIn.invoiceInTax(); + const invoiceInIntrastat = invoiceIn.invoiceInIntrastat(); + + expect(invoiceInTax.length).toEqual(2); + expect(invoiceInIntrastat.length).toEqual(2); }); }); diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/corrective.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/corrective.spec.js new file mode 100644 index 000000000..1047cd028 --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/specs/corrective.spec.js @@ -0,0 +1,49 @@ +const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); + +describe('invoiceIn corrective()', () => { + let ctx; + let options; + let tx; + + beforeEach(async() => { + ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'http://localhost'} + }, + args: {} + }; + + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: ctx.req + }); + + options = {transaction: tx}; + tx = await models.Sale.beginTransaction({}); + options.transaction = tx; + }); + + afterEach(async() => { + await tx.rollback(); + }); + + it('La función corrective debería devolver un id cuando se ejecuta correctamente', async() => { + const originalId = 1; + const invoiceReason = 3; + const invoiceType = 2; + const invoiceClass = 1; + const cloneId = await models.InvoiceIn.corrective(ctx, + originalId, invoiceReason, invoiceType, invoiceClass, options); + + expect(cloneId).toBeDefined(); + + const correction = await models.InvoiceInCorrection.findOne({ + where: {correctedFk: originalId, correctingFk: cloneId} + }, options); + + expect(correction.cplusRectificationTypeFk).toEqual(invoiceType); + expect(correction.siiTypeInvoiceOutFk).toEqual(invoiceClass); + expect(correction.invoiceCorrectionTypeFk).toEqual(invoiceReason); + }); +});