diff --git a/back/methods/dms/saveSign.js b/back/methods/dms/saveSign.js
new file mode 100644
index 000000000..f668c5ed2
--- /dev/null
+++ b/back/methods/dms/saveSign.js
@@ -0,0 +1,215 @@
+const md5 = require('md5');
+const fs = require('fs-extra');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('saveSign', {
+ description: 'Save sign',
+ accessType: 'WRITE',
+ accepts:
+ [
+ {
+ arg: 'signContent',
+ type: 'string',
+ required: true,
+ description: 'The sign content'
+ }, {
+ arg: 'tickets',
+ type: ['number'],
+ required: true,
+ description: 'The tickets'
+ }, {
+ arg: 'signedTime',
+ type: 'date',
+ description: 'The signed time'
+ }, {
+ arg: 'addressFk',
+ type: 'number',
+ required: true,
+ description: 'The address fk'
+ }
+ ],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/saveSign`,
+ verb: 'POST'
+ }
+ });
+
+ async function createGestDoc(ticketId, userFk) {
+ const models = Self.app.models;
+ if (!await gestDocExists(ticketId)) {
+ const result = await models.Ticket.findOne({
+ where: {
+ id: ticketId
+ },
+ include: [
+ {
+ relation: 'warehouse',
+ scope: {
+ fields: ['id']
+ }
+ }, {
+ relation: 'client',
+ scope: {
+ fields: ['name']
+ }
+ }, {
+ relation: 'route',
+ scope: {
+ fields: ['id']
+ }
+ }
+ ]
+ });
+
+ const warehouseFk = result.warehouseFk;
+ const companyFk = result.companyFk;
+ const client = result.client.name;
+ const route = result.route.id;
+
+ const resultDmsType = await models.DmsType.findOne({
+ where: {
+ code: 'Ticket'
+ }
+ });
+
+ const resultDms = await models.Dms.create({
+ dmsTypeFk: resultDmsType.id,
+ reference: ticketId,
+ description: `Ticket ${ticketId} Cliente ${client} Ruta ${route}`,
+ companyFk: companyFk,
+ warehouseFk: warehouseFk,
+ workerFk: userFk
+ });
+
+ return resultDms.insertId;
+ }
+ }
+
+ async function gestDocExists(ticket) {
+ const models = Self.app.models;
+ const result = await models.TicketDms.findOne({
+ where: {
+ ticketFk: ticket
+ },
+ fields: ['dmsFk']
+ });
+
+ if (result == null)
+ return false;
+
+ const isSigned = await models.Ticket.findOne({
+ where: {
+ id: ticket
+ },
+ fields: ['isSigned']
+ });
+
+ if (isSigned)
+ return true;
+ else
+ await models.Dms.destroyById(ticket);
+ }
+
+ async function dmsRecover(ticket, signContent) {
+ const models = Self.app.models;
+ await models.DmsRecover.create({
+ ticketFk: ticket,
+ sign: signContent
+ });
+ }
+
+ async function ticketGestdoc(ticket, dmsFk) {
+ const models = Self.app.models;
+ models.TicketDms.replaceOrCreate({
+ ticketFk: ticket,
+ dmsFk: dmsFk
+ });
+
+ const queryVnTicketSetState = `CALL vn.ticket_setState(?, ?)`;
+
+ await Self.rawSql(queryVnTicketSetState, [ticket, 'DELIVERED']);
+ }
+
+ async function updateGestdoc(file, ticket) {
+ const models = Self.app.models;
+ models.Dms.updateOne({
+ where: {
+ id: ticket
+ },
+ file: file,
+ contentType: 'image/png'
+ });
+ }
+
+ Self.saveSign = async(ctx, signContent, tickets, signedTime) => {
+ const models = Self.app.models;
+ let tx = await Self.beginTransaction({});
+ try {
+ const userId = ctx.req.accessToken.userId;
+
+ const dmsDir = `storage/dms`;
+
+ let image = null;
+
+ for (let i = 0; i < tickets.length; i++) {
+ const alertLevel = await models.TicketState.findOne({
+ where: {
+ ticketFk: tickets[i]
+ },
+ fields: ['alertLevel']
+ });
+
+ signedTime ? signedTime != undefined : signedTime = new Date();
+
+ if (alertLevel >= 2) {
+ let dir;
+ let id = null;
+ let fileName = null;
+
+ if (!await gestDocExists(tickets[i])) {
+ id = await createGestDoc(tickets[i], userId);
+
+ const hashDir = md5(id).substring(0, 3);
+ dir = `${dmsDir}/${hashDir}`;
+
+ if (!fs.existsSync(dir))
+ fs.mkdirSync(dir);
+
+ fileName = `${id}.png`;
+ image = `${dir}/${fileName}`;
+ } else
+
+ if (image != null) {
+ if (!fs.existsSync(dir))
+ dmsRecover(tickets[i], signContent);
+ else {
+ fs.writeFile(image, signContent, 'base64', async function(err) {
+ if (err) {
+ await tx.rollback();
+ return err.message;
+ }
+ });
+ }
+ } else
+ dmsRecover(tickets[i], signContent);
+
+ if (id != null && fileName.length > 0) {
+ ticketGestdoc(tickets[i], id);
+ updateGestdoc(id, fileName);
+ }
+ }
+ }
+
+ if (tx) await tx.commit();
+
+ return 'OK';
+ } catch (err) {
+ await tx.rollback();
+ throw err.message;
+ }
+ };
+};
diff --git a/back/models/dms.js b/back/models/dms.js
index 24c072f56..fc586201f 100644
--- a/back/models/dms.js
+++ b/back/models/dms.js
@@ -6,6 +6,7 @@ module.exports = Self => {
require('../methods/dms/removeFile')(Self);
require('../methods/dms/updateFile')(Self);
require('../methods/dms/deleteTrashFiles')(Self);
+ require('../methods/dms/saveSign')(Self);
Self.checkRole = async function(ctx, id) {
const models = Self.app.models;
diff --git a/db/changes/10510-december/00-mdbApp.sql b/db/changes/10510-december/00-mdbApp.sql
new file mode 100644
index 000000000..3202e3f08
--- /dev/null
+++ b/db/changes/10510-december/00-mdbApp.sql
@@ -0,0 +1,11 @@
+CREATE TABLE `vn`.`mdbApp` (
+ `app` varchar(100) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `baselineBranchFk` varchar(255) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
+ `userFk` int(10) unsigned DEFAULT NULL,
+ `locked` datetime DEFAULT NULL,
+ PRIMARY KEY (`app`),
+ KEY `mdbApp_FK` (`userFk`),
+ KEY `mdbApp_FK_1` (`baselineBranchFk`),
+ CONSTRAINT `mdbApp_FK` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
+ CONSTRAINT `mdbApp_FK_1` FOREIGN KEY (`baselineBranchFk`) REFERENCES `mdbBranch` (`name`) ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci
diff --git a/db/changes/225001/00-aclMdbApp.sql b/db/changes/225001/00-aclMdbApp.sql
new file mode 100644
index 000000000..b5b60546c
--- /dev/null
+++ b/db/changes/225001/00-aclMdbApp.sql
@@ -0,0 +1,4 @@
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES
+ ('MdbApp', 'lock', 'WRITE', 'ALLOW', 'ROLE', 'developer'),
+ ('MdbApp', 'unlock', 'WRITE', 'ALLOW', 'ROLE', 'developer');
diff --git a/db/changes/225201/00-ACL_saveSign.sql b/db/changes/225201/00-ACL_saveSign.sql
new file mode 100644
index 000000000..16f9931c4
--- /dev/null
+++ b/db/changes/225201/00-ACL_saveSign.sql
@@ -0,0 +1 @@
+INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`) VALUES ('Dms','saveSign','*','ALLOW','employee');
diff --git a/db/changes/225201/00-accountingMovements_add.sql b/db/changes/225201/00-accountingMovements_add.sql
new file mode 100644
index 000000000..12dc176bc
--- /dev/null
+++ b/db/changes/225201/00-accountingMovements_add.sql
@@ -0,0 +1,438 @@
+DROP PROCEDURE IF EXISTS `sage`.`accountingMovements_add`;
+
+DELIMITER $$
+$$
+CREATE DEFINER=`root`@`localhost` PROCEDURE `sage`.`accountingMovements_add`(vYear INT, vCompanyFk INT)
+BEGIN
+/**
+ * Traslada la info de contabilidad generada en base a vn.XDiario a la tabla sage.movConta para poder ejecutar posteriormente el proceso de importación de datos de SQL Server
+ * Solo traladará los asientos marcados con el campo vn.XDiario.enlazadoSage = FALSE
+ * @vYear Año contable del que se quiere trasladar la información
+ * @vCompanyFk Empresa de la que se quiere trasladar datos
+ */
+ DECLARE vDatedFrom DATETIME;
+ DECLARE vDatedTo DATETIME;
+ DECLARE vDuaTransactionFk INT;
+ DECLARE vTaxImportFk INT;
+ DECLARE vTaxImportReducedFk INT;
+ DECLARE vTaxImportSuperReducedFk INT;
+ DECLARE vTransactionExportFk INT;
+ DECLARE vTransactionExportTaxFreeFk INT;
+ DECLARE vSerialDua VARCHAR(1) DEFAULT 'D';
+ DECLARE vInvoiceTypeInformativeCode VARCHAR(1);
+ DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2) ;
+ DECLARE vBookEntries TEXT;
+
+ SELECT SiglaNacion INTO vCountryCanariasCode
+ FROM Naciones
+ WHERE Nacion ='ISLAS CANARIAS';
+
+ SELECT SiglaNacion INTO vCountryCeutaMelillaCode
+ FROM Naciones
+ WHERE Nacion ='CEUTA Y MELILLA';
+
+ SELECT CodigoTransaccion INTO vDuaTransactionFk
+ FROM TiposTransacciones
+ WHERE Transaccion = 'Import. bienes y serv. corrientes pdte. liquidar';
+
+ SELECT CodigoIva INTO vTaxImportFk
+ FROM TiposIva
+ WHERE Iva = 'IVA 21% importaciones';
+
+ SELECT CodigoIva INTO vTaxImportReducedFk
+ FROM TiposIva
+ WHERE Iva = 'IVA 10% importaciones';
+
+ SELECT CodigoIva INTO vTaxImportSuperReducedFk
+ FROM TiposIva
+ WHERE Iva = 'H.P. IVA Soportado Impor 4%';
+
+ SELECT CodigoTransaccion INTO vTransactionExportFk
+ FROM TiposTransacciones
+ WHERE Transaccion = 'Exportaciones definitivas';
+
+ SELECT CodigoTransaccion INTO vTransactionExportTaxFreeFk
+ FROM TiposTransacciones
+ WHERE Transaccion = 'Envíos definitivos a Canarias, Ceuta y Melilla';
+
+ SELECT codeSage INTO vInvoiceTypeInformativeCode
+ FROM invoiceType WHERE code ='informative';
+
+ SELECT CAST(CONCAT(vYear, '-01-01') AS DATETIME), util.dayEnd(CAST(CONCAT(vYear, '-12-31') AS DATE))
+ INTO vDatedFrom, vDatedTo;
+
+ TRUNCATE movContaIVA;
+
+ DELETE FROM movConta
+ WHERE enlazadoSage = FALSE
+ AND Asiento <> 1 ;
+
+ CALL clientSupplier_add(vCompanyFk);
+ CALL pgc_add(vCompanyFk);
+ CALL invoiceOut_manager(vYear, vCompanyFk);
+ CALL invoiceIn_manager(vYear, vCompanyFk);
+
+ INSERT INTO movConta(TipoEntrada,
+ Ejercicio,
+ CodigoEmpresa,
+ Asiento,
+ CargoAbono,
+ CodigoCuenta,
+ Contrapartida,
+ FechaAsiento,
+ Comentario,
+ ImporteAsiento,
+ NumeroPeriodo,
+ FechaGrabacion,
+ CodigoDivisa,
+ ImporteCambio,
+ ImporteDivisa,
+ FactorCambio,
+ IdProcesoIME,
+ TipoCarteraIME,
+ TipoAnaliticaIME,
+ StatusTraspasadoIME,
+ TipoImportacionIME,
+ Metalico347,
+ BaseIva1,
+ PorBaseCorrectora1,
+ PorIva1,
+ CuotaIva1,
+ PorRecargoEquivalencia1,
+ RecargoEquivalencia1,
+ CodigoTransaccion1,
+ BaseIva2,
+ PorBaseCorrectora2,
+ PorIva2,
+ CuotaIva2,
+ PorRecargoEquivalencia2,
+ RecargoEquivalencia2,
+ CodigoTransaccion2,
+ BaseIva3,
+ PorBaseCorrectora3,
+ PorIva3,
+ CuotaIva3,
+ PorRecargoEquivalencia3,
+ RecargoEquivalencia3,
+ CodigoTransaccion3,
+ BaseIva4,
+ PorBaseCorrectora4,
+ PorIva4,
+ CuotaIva4,
+ PorRecargoEquivalencia4,
+ RecargoEquivalencia4,
+ CodigoTransaccion4,
+ Año,
+ Serie,
+ Factura,
+ SuFacturaNo,
+ FechaFactura,
+ ImporteFactura,
+ TipoFactura,
+ CodigoCuentaFactura,
+ CifDni,
+ Nombre,
+ CodigoRetencion,
+ BaseRetencion,
+ PorRetencion,
+ ImporteRetencion,
+ SiglaNacion,
+ EjercicioFactura,
+ FechaOperacion,
+ Exclusion347,
+ MantenerAsiento,
+ ClaveOperacionFactura_,
+ TipoRectificativa,
+ FechaFacturaOriginal,
+ BaseImponibleOriginal,
+ CuotaIvaOriginal,
+ ClaseAbonoRectificativas,
+ RecargoEquivalenciaOriginal,
+ LibreA1,
+ CodigoIva1,
+ CodigoIva2,
+ CodigoIva3,
+ CodigoIva4,
+ IvaDeducible1,
+ IvaDeducible2,
+ IvaDeducible3,
+ IvaDeducible4,
+ Intracomunitaria
+ )
+ SELECT 'EN' TipoEntrada,
+ YEAR(x.FECHA) Ejercicio,
+ company_getCode(vCompanyFk) AS CodigoEmpresa,
+ x.ASIEN Asiento,
+ IF(EURODEBE <> 0 OR (EURODEBE = 0 AND EUROHABER IS NULL), 'D', 'H') CargoAbono,
+ x.SUBCTA CodigoCuenta,
+ x.CONTRA Contrapartida,
+ x.FECHA FechaAsiento,
+ x.CONCEPTO Comentario,
+ IF(x.EURODEBE, x.EURODEBE, x.EUROHABER) ImporteAsiento,
+ MONTH(x.FECHA) NumeroPeriodo,
+ IF(sub2.FECREGCON IS NULL, sub2.FECHA_EX, sub2.FECREGCON) FechaGrabacion,
+ IF(x.CAMBIO, IFNULL(mci.CodigoDivisa, sub3.code), '') CodigoDivisa,
+ x.CAMBIO ImporteCambio,
+ IFNULL(x.DEBEME, x.HABERME) ImporteDivisa,
+ IF(x.CAMBIO, TRUE, FALSE) FactorCambio,
+ NULL IdProcesoIME,
+ 0 TipoCarteraIME,
+ 0 TipoAnaliticaIME,
+ 0 StatusTraspasadoIME,
+ 0 TipoImportacionIME,
+ x.METAL Metalico347,
+ mci.BaseIva1,
+ mci.PorBaseCorrectora1,
+ mci.PorIva1,
+ mci.CuotaIva1,
+ mci.PorRecargoEquivalencia1,
+ mci.RecargoEquivalencia1,
+ mci.CodigoTransaccion1,
+ mci.BaseIva2,
+ mci.PorBaseCorrectora2,
+ mci.PorIva2,
+ mci.CuotaIva2,
+ mci.PorRecargoEquivalencia2,
+ mci.RecargoEquivalencia2,
+ mci.CodigoTransaccion2,
+ mci.BaseIva3,
+ mci.PorBaseCorrectora3,
+ mci.PorIva3,
+ mci.CuotaIva3,
+ mci.PorRecargoEquivalencia3,
+ mci.RecargoEquivalencia3,
+ mci.CodigoTransaccion3,
+ mci.BaseIva4,
+ mci.PorBaseCorrectora4,
+ mci.PorIva4,
+ mci.CuotaIva4,
+ mci.PorRecargoEquivalencia4,
+ mci.RecargoEquivalencia4,
+ mci.CodigoTransaccion4,
+ mci.Año,
+ mci.Serie,
+ mci.Factura,
+ mci.SuFacturaNo,
+ mci.FechaFactura,
+ mci.ImporteFactura,
+ mci.TipoFactura,
+ mci.CodigoCuentaFactura,
+ mci.CifDni,
+ mci.Nombre,
+ mci.CodigoRetencion,
+ mci.BaseRetencion,
+ mci.PorRetencion,
+ mci.ImporteRetencion,
+ mci.SiglaNacion,
+ mci.EjercicioFactura,
+ mci.FechaOperacion,
+ mci.Exclusion347,
+ TRUE,
+ mci.ClaveOperacionFactura,
+ mci.TipoRectificativa,
+ mci.FechaFacturaOriginal,
+ mci.BaseImponibleOriginal,
+ mci.CuotaIvaOriginal,
+ mci.ClaseAbonoRectificativas,
+ mci.RecargoEquivalenciaOriginal,
+ mci.LibreA1,
+ mci.CodigoIva1,
+ mci.CodigoIva2,
+ mci.CodigoIva3,
+ mci.CodigoIva4,
+ mci.IvaDeducible1,
+ mci.IvaDeducible2,
+ mci.IvaDeducible3,
+ mci.IvaDeducible4,
+ mci.Intracomunitaria
+ FROM vn.XDiario x
+ LEFT JOIN movContaIVA mci ON mci.id = x.id
+ LEFT JOIN (SELECT *
+ FROM (SELECT DISTINCT ASIEN, FECREGCON, FECHA_EX
+ FROM vn.XDiario
+ WHERE enlazadoSage = FALSE
+ ORDER BY ASIEN, FECREGCON DESC, FECHA_EX DESC
+ LIMIT 10000000000000000000
+ ) sub GROUP BY ASIEN
+ )sub2 ON sub2.ASIEN = x.ASIEN
+ LEFT JOIN ( SELECT DISTINCT(account),cu.code
+ FROM vn.bank b
+ JOIN vn.currency cu ON cu.id = b.currencyFk
+ WHERE cu.code <> 'EUR' -- no se informa cuando la divisa en EUR
+ )sub3 ON sub3.account = x.SUBCTA
+ WHERE x.enlazadoSage = FALSE
+ AND x.empresa_id = vCompanyFk
+ AND x.FECHA BETWEEN vDatedFrom AND vDatedTo;
+
+-- Metálicos
+ UPDATE movConta m
+ JOIN (SELECT Asiento,
+ c.socialName name,
+ c.fi,
+ n.SiglaNacion,
+ m.CodigoCuenta,
+ m.Contrapartida
+ FROM movConta m
+ LEFT JOIN vn.client c ON c.id = IF(m.CargoAbono = 'H',
+ CAST(SUBSTRING(m.CodigoCuenta, 3, LENGTH(m.CodigoCuenta)) AS UNSIGNED),
+ CAST(SUBSTRING(m.Contrapartida, 3, LENGTH(m.Contrapartida)) AS UNSIGNED))
+ LEFT JOIN Naciones n ON n.countryFk = c.countryFk
+ WHERE m.Metalico347 = TRUE
+ AND m.enlazadoSage = FALSE
+ )sub ON m.Asiento = sub.Asiento
+ SET m.Metalico347 = TRUE,
+ m.TipoFactura = vInvoiceTypeInformativeCode,
+ m.CifDni = sub.fi,
+ m.Nombre = sub.name,
+ m.SiglaNacion = sub.SiglaNacion
+ WHERE m.enlazadoSage = FALSE;
+
+ UPDATE movConta m
+ SET m.Metalico347 = FALSE,
+ m.TipoFactura = ''
+ WHERE m.CargoAbono = 'D'
+ AND m.enlazadoSage = FALSE;
+
+-- Elimina cuentas de cliente/proveedor que no se utilizarán en la importación
+ DELETE cp
+ FROM clientesProveedores cp
+ LEFT JOIN movConta mc ON mc.codigoCuenta = cp.codigoCuenta
+ AND mc.enlazadoSage = FALSE
+ WHERE mc.codigoCuenta IS NULL;
+
+-- Elimina cuentas contables que no se utilizarán en la importación
+ DELETE pc
+ FROM planCuentasPGC pc
+ LEFT JOIN movConta mc ON mc.codigoCuenta = pc.codigoCuenta
+ AND mc.enlazadoSage = FALSE
+ WHERE mc.codigoCuenta IS NULL;
+
+-- DUAS
+ UPDATE movConta mci
+ JOIN vn.XDiario x ON x.ASIEN = mci.Asiento
+ JOIN TiposIva ti ON ti.CodigoIva = x.IVA
+ JOIN vn.pgcMaster pm ON pm.code = mci.CodigoCuenta COLLATE utf8mb3_unicode_ci
+ SET mci.BaseIva1 = x.BASEEURO,
+ mci.PorIva1 = x.IVA,
+ mci.CuotaIva1 = CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2)),
+ mci.CodigoTransaccion1 = vDuaTransactionFk,
+ mci.CodigoIva1 = vTaxImportReducedFk,
+ mci.IvaDeducible1 = TRUE,
+ mci.FechaFacturaOriginal = x.FECHA_EX,
+ mci.SuFacturaNo = x.FACTURAEX,
+ mci.FechaOperacion = x.FECHA_OP,
+ mci.ImporteFactura = mci.ImporteFactura + x.BASEEURO + CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
+ WHERE pm.description = 'HP Iva pendiente'
+ AND mci.enlazadoSage = FALSE
+ AND x.SERIE = vSerialDua COLLATE utf8mb3_unicode_ci
+ AND ti.Iva = 'I.V.A. 10% Nacional';
+
+ UPDATE movConta mci
+ JOIN vn.XDiario x ON x.ASIEN = mci.Asiento
+ JOIN TiposIva ti ON ti.CodigoIva = x.IVA
+ JOIN vn.pgcMaster pm ON pm.code = mci.CodigoCuenta COLLATE utf8mb3_unicode_ci
+ SET mci.BaseIva2 = x.BASEEURO ,
+ mci.PorIva2 = x.IVA,
+ mci.CuotaIva2 = CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10,2)),
+ mci.CodigoTransaccion2 = vDuaTransactionFk ,
+ mci.CodigoIva2 = vTaxImportFk,
+ mci.IvaDeducible2 = TRUE,
+ mci.ImporteFactura = mci.ImporteFactura + x.BASEEURO + CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
+ WHERE pm.description = 'HP Iva pendiente'
+ AND mci.enlazadoSage = FALSE
+ AND x.SERIE = vSerialDua COLLATE utf8mb3_unicode_ci
+ AND ti.Iva = 'I.V.A. 21%';
+
+ UPDATE movConta mci
+ JOIN vn.XDiario x ON x.ASIEN = mci.Asiento
+ JOIN TiposIva ti ON ti.CodigoIva = x.IVA
+ JOIN vn.pgcMaster pm ON pm.code = mci.CodigoCuenta COLLATE utf8mb3_unicode_ci
+ SET mci.BaseIva3 = x.BASEEURO ,
+ mci.PorIva3 = x.IVA,
+ mci.CuotaIva3 = CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10,2)),
+ mci.CodigoTransaccion3 = vDuaTransactionFk ,
+ mci.CodigoIva3 = vTaxImportSuperReducedFk,
+ mci.IvaDeducible3 = TRUE,
+ mci.ImporteFactura = mci.ImporteFactura + x.BASEEURO + CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
+ WHERE pm.description = 'HP Iva pendiente'
+ AND mci.enlazadoSage = FALSE
+ AND x.SERIE = vSerialDua COLLATE utf8mb3_unicode_ci
+ AND ti.Iva = 'I.V.A. 4%';
+
+-- Rectificativas
+ UPDATE movConta mci
+ JOIN (SELECT x.ASIEN, x.FECHA_RT, x.SERIE_RT, x.FACTU_RT
+ FROM movConta mci
+ JOIN vn.XDiario x ON x.ASIEN = mci.Asiento
+ WHERE mci.TipoRectificativa > 0
+ AND mci.enlazadoSage = FALSE
+ AND x.FACTU_RT IS NOT NULL
+ GROUP BY x.ASIEN
+ ) sub ON sub.ASIEN = mci.Asiento
+ SET mci.EjercicioFacturaOriginal = YEAR(sub.FECHA_RT),
+ mci.SerieFacturaOriginal = sub.SERIE_RT,
+ mci.NumeroFacturaOriginal = sub.FACTU_RT
+ WHERE mci.TipoRectificativa > 0 AND
+ mci.enlazadoSage = FALSE ;
+
+-- Exportaciones Andorras y Canarias cambia TT (la cuenta es compartida)
+ UPDATE movConta mci
+ SET CodigoTransaccion1 = vTransactionExportTaxFreeFk,
+ CodigoTransaccion2 = IF(CodigoTransaccion2 = 0, 0, vTransactionExportTaxFreeFk),
+ CodigoTransaccion3 = IF(CodigoTransaccion3 = 0, 0, vTransactionExportTaxFreeFk),
+ CodigoTransaccion4 = IF(CodigoTransaccion4 = 0, 0, vTransactionExportTaxFreeFk)
+ WHERE enlazadoSage = FALSE
+ AND (CodigoTransaccion1 = vTransactionExportFk
+ OR CodigoTransaccion2 = vTransactionExportFk
+ OR CodigoTransaccion3 = vTransactionExportFk
+ OR CodigoTransaccion4 = vTransactionExportFk)
+ AND SiglaNacion IN (vCountryCanariasCode COLLATE utf8mb3_unicode_ci, vCountryCeutaMelillaCode COLLATE utf8mb3_unicode_ci);
+
+ UPDATE movConta mc
+ SET CodigoDivisa = 'USD',
+ FactorCambio = TRUE,
+ ImporteCambio = ABS( CAST( IF( ImporteDivisa <> 0 AND ImporteCambio = 0, ImporteAsiento / ImporteDivisa, ImporteCambio) AS DECIMAL( 10, 2)))
+ WHERE enlazadoSage = FALSE
+ AND (ImporteCambio <> 0 OR ImporteDivisa <> 0 OR FactorCambio);
+
+ UPDATE movConta mc
+ SET importeDivisa= -importeDivisa
+ WHERE enlazadoSage = FALSE
+ AND importeDivisa > 0
+ AND ImporteAsiento < 0;
+
+ -- Comprobación que los importes e ivas sean correctos, avisa vía CAU
+ SELECT GROUP_CONCAT(Asiento ORDER BY Asiento ASC SEPARATOR ',') INTO vBookEntries
+ FROM(SELECT sub.Asiento
+ FROM (SELECT mc.Asiento, SUM(mc.ImporteAsiento) amount
+ FROM movConta mc
+ WHERE mc.enlazadoSage = FALSE
+ GROUP BY mc.Asiento)sub
+ JOIN (SELECT x.ASIEN, SUM(IFNULL(x.EURODEBE,0) + IFNULL(x.EUROHABER,0)) amount
+ FROM vn.XDiario x
+ WHERE x.enlazadoSage = FALSE
+ GROUP BY ASIEN)sub2 ON sub2.ASIEN = sub.Asiento
+ WHERE sub.amount <> sub2.amount
+ UNION ALL
+ SELECT sub.Asiento
+ FROM (SELECT Asiento, SUM(BaseIva1 + BaseIva2 + BaseIva3 + BaseIva4) amountTaxableBase
+ FROM movConta
+ WHERE TipoFactura <> 'I'
+ AND enlazadoSage = FALSE
+ GROUP BY Asiento) sub
+ JOIN (SELECT ASIEN, SUM(BASEEURO) amountTaxableBase
+ FROM (SELECT ASIEN, SUM(BASEEURO) BASEEURO
+ FROM vn.XDiario
+ WHERE FACTURA
+ AND auxiliar <> '*'
+ AND enlazadoSage = FALSE
+ GROUP BY FACTURA, ASIEN)sub3
+ GROUP BY ASIEN) sub2 ON sub2.ASIEN = sub.Asiento
+ WHERE sub.amountTaxableBase<>sub2.amountTaxableBase
+ AND sub.amountTaxableBase/2 <> sub2.amountTaxableBase) sub;
+
+ IF vBookEntries IS NOT NULL THEN
+ SELECT util.notification_send ("book-entries-imported-incorrectly", CONCAT('{"bookEntries":"', vBookEntries,'"}'), null);
+ END IF;
+END$$
+DELIMITER ;
diff --git a/db/changes/225201/00-notification_send.sql b/db/changes/225201/00-notification_send.sql
new file mode 100644
index 000000000..a422cebac
--- /dev/null
+++ b/db/changes/225201/00-notification_send.sql
@@ -0,0 +1,24 @@
+DROP FUNCTION IF EXISTS `util`.`notification_send`;
+
+DELIMITER $$
+$$
+CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`notification_send`(vNotificationName VARCHAR(255), vParams TEXT, vAuthorFk INT) RETURNS int(11)
+ MODIFIES SQL DATA
+BEGIN
+/**
+ * Sends a notification.
+ *
+ * @param vNotificationName The notification name
+ * @param vParams The notification parameters formatted as JSON
+ * @param vAuthorFk The notification author or %NULL if there is no author
+ * @return The notification id
+ */
+
+ INSERT INTO notificationQueue
+ SET notificationFk = vNotificationName,
+ params = vParams,
+ authorFk = vAuthorFk;
+
+ RETURN LAST_INSERT_ID();
+END$$
+DELIMITER ;
diff --git a/db/changes/225201/00-utilNotification.sql b/db/changes/225201/00-utilNotification.sql
new file mode 100644
index 000000000..5c80b81b3
--- /dev/null
+++ b/db/changes/225201/00-utilNotification.sql
@@ -0,0 +1,4 @@
+INSERT INTO `util`.`notification` (id, name, description) VALUES(3, 'book-entries-imported-incorrectly', 'accounting entries exported incorrectly');
+INSERT INTO `util`.`notificationAcl` (notificationFk, roleFk) VALUES(3, 5);
+INSERT INTO `util`.`notificationSubscription` (notificationFk, userFk) VALUES(3, 19663);
+
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index c4ce78658..762e5411a 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -1335,9 +1335,9 @@ INSERT INTO `vn`.`itemTypeTag`(`id`, `itemTypeFk`, `tagFk`, `priority`)
CALL `vn`.`itemRefreshTags`(NULL);
-INSERT INTO `vn`.`itemLog` (`id`, `originFk`, `userFk`, `action`, `description`)
+INSERT INTO `vn`.`itemLog` (`id`, `originFk`, `userFk`, `action`, `description`, `changedModel`, `oldInstance`, `newInstance`, `changedModelId`, `changedModelValue`)
VALUES
- ('1', '1', '1', 'insert', 'We made a change!');
+ ('1', '1', '1', 'insert', 'We made a change!', 'Item', '{}', '{}', 1, '1');
INSERT INTO `vn`.`recovery`(`id`, `clientFk`, `started`, `finished`, `amount`, `period`)
VALUES
@@ -2692,7 +2692,7 @@ INSERT INTO `util`.`notificationConfig`
INSERT INTO `util`.`notification` (`id`, `name`, `description`)
VALUES
(1, 'print-email', 'notification fixture one'),
- (3, 'supplier-pay-method-update', 'A supplier pay method has been updated');
+ (4, 'supplier-pay-method-update', 'A supplier pay method has been updated');
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
VALUES
@@ -2707,7 +2707,8 @@ INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `autho
INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`)
VALUES
(1, 1109),
- (1, 1110);
+ (1, 1110),
+ (3, 1109);
INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`)
VALUES
@@ -2745,10 +2746,15 @@ INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`
VALUES
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', 'open', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
+INSERT INTO `vn`.`mdbApp` (`app`, `baselineBranchFk`, `userFk`, `locked`)
+ VALUES
+ ('foo', 'master', NULL, NULL),
+ ('bar', 'test', 9, util.VN_NOW());
INSERT INTO `vn`.`ticketLog` (`id`, `originFk`, `userFk`, `action`, `changedModel`, `oldInstance`, `newInstance`, `changedModelId`)
VALUES
(1, 1, 9, 'insert', 'Ticket', '{}', '{"clientFk":1, "nickname": "Bat cave"}', 1);
+
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
VALUES
('lilium', 'dev', 'http://localhost:8080/#/'),
diff --git a/db/dump/structure.sql b/db/dump/structure.sql
index 9a77662ea..47fdd6d74 100644
--- a/db/dump/structure.sql
+++ b/db/dump/structure.sql
@@ -16340,6 +16340,185 @@ CREATE TABLE `invoiceType` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */;
+--
+-- Table structure for table `movConta`
+--
+
+DROP TABLE IF EXISTS `movConta`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `movConta` (
+ `OrdenMovimientos` int(11) NOT NULL AUTO_INCREMENT,
+ `MovPosicion` varchar(64) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Ejercicio` smallint(6) NOT NULL,
+ `CodigoEmpresa` smallint(6) NOT NULL,
+ `Asiento` int(11) NOT NULL,
+ `CargoAbono` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoCuenta` varchar(15) CHARACTER SET utf8mb3 NOT NULL,
+ `Contrapartida` varchar(15) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `FechaAsiento` datetime NOT NULL,
+ `TipoDocumento` varchar(6) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `DocumentoConta` varchar(9) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Comentario` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `ImporteAsiento` decimal(28,10) NOT NULL,
+ `CodigoDiario` smallint(6) NOT NULL,
+ `CodigoCanal` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoActividad` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `FechaVencimiento` datetime DEFAULT NULL,
+ `NumeroPeriodo` smallint(6) NOT NULL,
+ `CodigoUsuario` smallint(6) NOT NULL,
+ `FechaGrabacion` datetime NOT NULL,
+ `TipoEntrada` varchar(2) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoDepartamento` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoSeccion` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoDivisa` varchar(3) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `ImporteCambio` decimal(28,10) NOT NULL,
+ `ImporteDivisa` decimal(28,10) NOT NULL,
+ `FactorCambio` decimal(28,10) NOT NULL,
+ `CodigoProyecto` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `LibreN1` int(11) NOT NULL,
+ `LibreN2` int(11) NOT NULL,
+ `LibreA1` varchar(15) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `LibreA2` varchar(15) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdDelegacion` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `MovCartera` varchar(64) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
+ `IdProcesoIME` varchar(64) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `TipoCarteraIME` smallint(6) NOT NULL,
+ `TipoAnaliticaIME` smallint(6) NOT NULL,
+ `StatusTraspasadoIME` tinyint(4) NOT NULL,
+ `TipoImportacionIME` tinyint(4) NOT NULL,
+ `BaseIva1` decimal(28,10) NOT NULL,
+ `PorBaseCorrectora1` decimal(28,10) NOT NULL,
+ `PorIva1` decimal(28,10) NOT NULL,
+ `CuotaIva1` decimal(28,10) NOT NULL,
+ `PorRecargoEquivalencia1` decimal(28,10) NOT NULL,
+ `RecargoEquivalencia1` decimal(28,10) NOT NULL,
+ `CodigoTransaccion1` tinyint(4) NOT NULL,
+ `BaseIva2` decimal(28,10) NOT NULL,
+ `PorBaseCorrectora2` decimal(28,10) NOT NULL,
+ `PorIva2` decimal(28,10) NOT NULL,
+ `CuotaIva2` decimal(28,10) NOT NULL,
+ `PorRecargoEquivalencia2` decimal(28,10) NOT NULL,
+ `RecargoEquivalencia2` decimal(28,10) NOT NULL,
+ `CodigoTransaccion2` tinyint(4) NOT NULL,
+ `BaseIva3` decimal(28,10) NOT NULL,
+ `PorBaseCorrectora3` decimal(28,10) NOT NULL,
+ `PorIva3` decimal(28,10) NOT NULL,
+ `CuotaIva3` decimal(28,10) NOT NULL,
+ `PorRecargoEquivalencia3` decimal(28,10) NOT NULL,
+ `RecargoEquivalencia3` decimal(28,10) NOT NULL,
+ `CodigoTransaccion3` tinyint(4) NOT NULL,
+ `baseIva4` decimal(28,10) NOT NULL,
+ `PorBaseCorrectora4` decimal(28,10) NOT NULL,
+ `PorIva4` decimal(28,10) NOT NULL,
+ `CuotaIva4` decimal(28,10) NOT NULL,
+ `PorRecargoEquivalencia4` decimal(28,10) NOT NULL,
+ `RecargoEquivalencia4` decimal(28,10) NOT NULL,
+ `CodigoTransaccion4` tinyint(4) NOT NULL,
+ `Año` smallint(6) NOT NULL,
+ `Serie` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Factura` int(11) NOT NULL,
+ `SuFacturaNo` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `FechaFactura` datetime NOT NULL,
+ `ImporteFactura` decimal(28,10) NOT NULL,
+ `TipoFactura` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoCuentaFactura` varchar(15) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CifDni` varchar(13) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Nombre` varchar(35) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `CodigoRetencion` smallint(6) NOT NULL,
+ `BaseRetencion` decimal(28,10) NOT NULL,
+ `PorRetencion` decimal(28,10) NOT NULL,
+ `ImporteRetencion` decimal(28,10) NOT NULL,
+ `AbonoIva` smallint(6) NOT NULL,
+ `CodigoActividadF` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Intracomunitaria` smallint(6) NOT NULL,
+ `CodigoTerritorio` smallint(6) NOT NULL,
+ `SiglaNacion` varchar(2) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `RetencionInformativa` smallint(6) NOT NULL,
+ `EjercicioFacturaOriginal` smallint(6) NOT NULL,
+ `SerieFacturaOriginal` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `NumeroFacturaOriginal` int(11) NOT NULL,
+ `EjercicioFactura` smallint(6) NOT NULL,
+ `CobroPagoRetencion` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `FechaOperacion` datetime NOT NULL,
+ `Exclusion347` smallint(6) NOT NULL,
+ `MovIdentificadorIME` varchar(64) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Previsiones` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `MantenerAsiento` tinyint(4) NOT NULL,
+ `OrdenMovIME` smallint(6) NOT NULL,
+ `Metalico347` smallint(6) NOT NULL,
+ `ClaveOperacionFactura_` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `SerieAgrupacion_` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `NumeroFacturaInicial_` int(11) NOT NULL,
+ `NumeroFacturaFinal_` int(11) NOT NULL,
+ `IdAsientoExterno` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdDiarioExterno` varchar(10) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdFacturaExterno` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdMovimiento` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdCuadre` smallint(6) NOT NULL,
+ `FechaCuadre` datetime NOT NULL,
+ `TipoCuadre` varchar(4) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `AgrupacionCuadre` int(11) NOT NULL,
+ `StatusSaldo` smallint(6) NOT NULL,
+ `StatusConciliacion` smallint(6) NOT NULL,
+ `CodigoConciliacion` int(11) NOT NULL,
+ `FechaConciliacion` datetime NOT NULL,
+ `TipoConciliacion` smallint(6) NOT NULL,
+ `IndicadorContaBanco` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion3` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion4` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion5` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion6` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion7` varchar(40) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion8` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion9` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion2` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Descripcion1` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `Punteo1` smallint(6) NOT NULL,
+ `Punteo9` smallint(6) NOT NULL,
+ `Punteo8` smallint(6) NOT NULL,
+ `Punteo7` smallint(6) NOT NULL,
+ `Punteo6` smallint(6) NOT NULL,
+ `Punteo5` smallint(6) NOT NULL,
+ `Punteo4` smallint(6) NOT NULL,
+ `Punteo3` smallint(6) NOT NULL,
+ `Punteo2` smallint(6) NOT NULL,
+ `CodigoIva1` smallint(6) NOT NULL,
+ `CodigoIva2` smallint(6) NOT NULL,
+ `CodigoIva3` smallint(6) NOT NULL,
+ `CodigoIva4` smallint(6) NOT NULL,
+ `CriterioIva` tinyint(4) NOT NULL,
+ `FechaMaxVencimiento` datetime NOT NULL,
+ `TipoCriterioCaja` tinyint(4) NOT NULL,
+ `MovFacturaOrigenIME` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdFacturaExternoFinal` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdFacturaExternoInicial` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IdFacturaExternoOriginal` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `NumFacturasExternoAgrupacion` int(11) NOT NULL,
+ `CodigoMedioCobro` varchar(1) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `MedioCobro` varchar(31) COLLATE utf8mb3_unicode_ci NOT NULL,
+ `IvaDeducible1` smallint(6) NOT NULL DEFAULT 1,
+ `IvaDeducible2` smallint(6) NOT NULL DEFAULT 1,
+ `IvaDeducible3` smallint(6) NOT NULL DEFAULT 1,
+ `IvaDeducible4` smallint(6) NOT NULL DEFAULT 1,
+ `TipoRectificativa` smallint(6) NOT NULL,
+ `FechaFacturaOriginal` datetime NOT NULL,
+ `BaseImponibleOriginal` decimal(28,10) NOT NULL,
+ `CuotaIvaOriginal` decimal(28,10) NOT NULL,
+ `ClaseAbonoRectificativas` smallint(6) NOT NULL,
+ `RecargoEquivalenciaOriginal` decimal(28,10) NOT NULL,
+ `ObjetoFactura` text COLLATE utf8mb3_unicode_ci NOT NULL,
+ `enlazadoSage` tinyint(1) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`OrdenMovimientos`,`LibreN1`),
+ KEY `ix_movconta2` (`IdProcesoIME`),
+ KEY `CodigoCuenta` (`CodigoCuenta`),
+ KEY `movConta_Asiento` (`Asiento`),
+ KEY `ix_movconta` (`enlazadoSage`,`IdProcesoIME`),
+ KEY `movConta_IdProcesoIME` (`IdProcesoIME`),
+ KEY `movConta_Asiento2` (`Asiento`,`IdProcesoIME`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `movContaIVA`
--
diff --git a/db/export-structure.sh b/db/export-structure.sh
index f52819c54..a4fd4a8c6 100755
--- a/db/export-structure.sh
+++ b/db/export-structure.sh
@@ -84,7 +84,6 @@ IGNORETABLES=(
--ignore-table=vn.warehouseJoined
--ignore-table=vn.workerTeam__
--ignore-table=vn.XDiario__
- --ignore-table=sage.movConta
--ignore-table=sage.movContaCopia
)
mysqldump \
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 9dab10673..f6a299011 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -311,10 +311,12 @@ export default {
firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr'
},
clientLog: {
- lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before',
- lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after',
- penultimateModificationPreviousValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.before',
- penultimateModificationCurrentValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.after'
+ lastModificationPreviousValue: 'vn-client-log vn-tr table tr td.before',
+ lastModificationCurrentValue: 'vn-client-log vn-tr table tr td.after',
+ namePreviousValue: 'vn-client-log vn-tr table tr:nth-child(1) td.before',
+ nameCurrentValue: 'vn-client-log vn-tr table tr:nth-child(1) td.after',
+ activePreviousValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.before',
+ activeCurrentValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.after'
},
clientBalance: {
@@ -518,7 +520,7 @@ export default {
},
itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
- fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) > vn-td > vn-one:nth-child(3) > div span:nth-child(2)',
+ fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(3) td.after',
},
ticketSummary: {
header: 'vn-ticket-summary > vn-card > h5',
@@ -711,9 +713,10 @@ export default {
ticketLog: {
firstTD: 'vn-ticket-log vn-table vn-td:nth-child(1)',
logButton: 'vn-left-menu a[ui-sref="ticket.card.log"]',
- firstLogEntry: 'vn-ticket-log vn-data-viewer vn-tbody vn-tr',
- changes: 'vn-ticket-log vn-data-viewer vn-tbody > vn-tr > vn-td:nth-child(7)',
- id: 'vn-ticket-log vn-tr:nth-child(1) vn-one:nth-child(1) span'
+ user: 'vn-ticket-log vn-tbody vn-tr vn-td:nth-child(2)',
+ action: 'vn-ticket-log vn-tbody vn-tr vn-td:nth-child(4)',
+ changes: 'vn-ticket-log vn-data-viewer vn-tbody vn-tr table tr:nth-child(2) td.after',
+ id: 'vn-ticket-log vn-tr:nth-child(1) table tr:nth-child(1) td.before'
},
ticketService: {
addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button',
@@ -1123,7 +1126,7 @@ export default {
undoChanges: 'vn-travel-basic-data vn-button[label="Undo changes"]'
},
travelLog: {
- firstLogFirstTD: 'vn-travel-log vn-tbody > vn-tr > vn-td:nth-child(1) > div'
+ firstLogFirstTD: 'vn-travel-log vn-tbody > vn-tr > vn-td:nth-child(5)'
},
travelThermograph: {
add: 'vn-travel-thermograph-index vn-float-button[icon="add"]',
diff --git a/e2e/paths/02-client/07_edit_web_access.spec.js b/e2e/paths/02-client/07_edit_web_access.spec.js
index 3d9ccee62..29b39f788 100644
--- a/e2e/paths/02-client/07_edit_web_access.spec.js
+++ b/e2e/paths/02-client/07_edit_web_access.spec.js
@@ -67,22 +67,22 @@ describe('Client Edit web access path', () => {
});
it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => {
- let lastModificationPreviousValue = await page
- .waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText');
- let lastModificationCurrentValue = await page
- .waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
+ let namePreviousValue = await page
+ .waitToGetProperty(selectors.clientLog.namePreviousValue, 'innerText');
+ let nameCurrentValue = await page
+ .waitToGetProperty(selectors.clientLog.nameCurrentValue, 'innerText');
- expect(lastModificationPreviousValue).toEqual('name MaxEisenhardt active false');
- expect(lastModificationCurrentValue).toEqual('name Legion active false');
+ expect(namePreviousValue).toEqual('MaxEisenhardt');
+ expect(nameCurrentValue).toEqual('Legion');
});
it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => {
- let penultimateModificationPreviousValue = await page
- .waitToGetProperty(selectors.clientLog.penultimateModificationPreviousValue, 'innerText');
- let penultimateModificationCurrentValue = await page
- .waitToGetProperty(selectors.clientLog.penultimateModificationCurrentValue, 'innerText');
+ let activePreviousValue = await page
+ .waitToGetProperty(selectors.clientLog.activePreviousValue, 'innerText');
+ let activeCurrentValue = await page
+ .waitToGetProperty(selectors.clientLog.activeCurrentValue, 'innerText');
- expect(penultimateModificationPreviousValue).toEqual('name MaxEisenhardt active true');
- expect(penultimateModificationCurrentValue).toEqual('name MaxEisenhardt active false');
+ expect(activePreviousValue).toEqual('✓');
+ expect(activeCurrentValue).toEqual('✗');
});
});
diff --git a/e2e/paths/02-client/13_log.spec.js b/e2e/paths/02-client/13_log.spec.js
index 9b047a47c..8f186d842 100644
--- a/e2e/paths/02-client/13_log.spec.js
+++ b/e2e/paths/02-client/13_log.spec.js
@@ -43,7 +43,7 @@ describe('Client log path', () => {
let lastModificationCurrentValue = await page.
waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
- expect(lastModificationPreviousValue).toEqual('name DavidCharlesHaller');
- expect(lastModificationCurrentValue).toEqual('name this is a test');
+ expect(lastModificationPreviousValue).toEqual('DavidCharlesHaller');
+ expect(lastModificationCurrentValue).toEqual('this is a test');
});
});
diff --git a/e2e/paths/05-ticket/02_expeditions_and_log.spec.js b/e2e/paths/05-ticket/02_expeditions_and_log.spec.js
index f970247e5..ae5e2fb0c 100644
--- a/e2e/paths/05-ticket/02_expeditions_and_log.spec.js
+++ b/e2e/paths/05-ticket/02_expeditions_and_log.spec.js
@@ -32,14 +32,17 @@ describe('Ticket expeditions and log path', () => {
it(`should confirm the expedition deleted is shown now in the ticket log`, async() => {
await page.accessToSection('ticket.card.log');
- const firstLogEntry = await page
- .waitToGetProperty(selectors.ticketLog.firstLogEntry, 'innerText');
+ const user = await page
+ .waitToGetProperty(selectors.ticketLog.user, 'innerText');
+
+ const action = await page
+ .waitToGetProperty(selectors.ticketLog.action, 'innerText');
const id = await page
.waitToGetProperty(selectors.ticketLog.id, 'innerText');
- expect(firstLogEntry).toContain('production');
- expect(firstLogEntry).toContain('Deletes');
+ expect(user).toContain('production');
+ expect(action).toContain('Deletes');
expect(id).toEqual('2');
});
});
diff --git a/e2e/paths/05-ticket/17_log.spec.js b/e2e/paths/05-ticket/17_log.spec.js
index 399eb647b..32829ee74 100644
--- a/e2e/paths/05-ticket/17_log.spec.js
+++ b/e2e/paths/05-ticket/17_log.spec.js
@@ -55,6 +55,6 @@ describe('Ticket log path', () => {
const result = await page.waitToGetProperty(selectors.ticketLog.firstTD, 'innerText');
- expect(result.length).toBeGreaterThan('20');
+ expect(result.length).toBeGreaterThan('15');
});
});
diff --git a/e2e/paths/13-supplier/02_basic_data.spec.js b/e2e/paths/13-supplier/02_basic_data.spec.js
index 4f3c49512..9d86e11d4 100644
--- a/e2e/paths/13-supplier/02_basic_data.spec.js
+++ b/e2e/paths/13-supplier/02_basic_data.spec.js
@@ -70,8 +70,8 @@ describe('Supplier basic data path', () => {
});
it('should check the changes have been recorded', async() => {
- const result = await page.waitToGetProperty('#newInstance:nth-child(3)', 'innerText');
+ const result = await page.waitToGetProperty('vn-tr table tr:nth-child(3) td.after', 'innerText');
- expect(result).toEqual('note Some notes');
+ expect(result).toEqual('Some notes');
});
});
diff --git a/front/core/components/crud-model/crud-model.js b/front/core/components/crud-model/crud-model.js
index 1095985dc..16b837d6a 100644
--- a/front/core/components/crud-model/crud-model.js
+++ b/front/core/components/crud-model/crud-model.js
@@ -14,6 +14,7 @@ export default class CrudModel extends ModelProxy {
this.$q = $q;
this.primaryKey = 'id';
this.autoLoad = false;
+ this.page = 1;
}
$onInit() {
@@ -125,13 +126,20 @@ export default class CrudModel extends ModelProxy {
}
}
- loadMore() {
+ loadMore(append) {
if (!this.moreRows)
return this.$q.resolve();
- let filter = Object.assign({}, this.currentFilter);
- filter.skip = this.orgData ? this.orgData.length : 0;
- return this.sendRequest(filter, true);
+ const filter = Object.assign({}, this.currentFilter);
+ if (append)
+ filter.skip = this.orgData ? this.orgData.length : 0;
+
+ if (!append) {
+ this.page += 1;
+ filter.limit = this.page * this.limit;
+ }
+
+ return this.sendRequest(filter, append);
}
clear() {
diff --git a/front/core/components/crud-model/index.spec.js b/front/core/components/crud-model/index.spec.js
index 8673e947f..7eab80405 100644
--- a/front/core/components/crud-model/index.spec.js
+++ b/front/core/components/crud-model/index.spec.js
@@ -148,7 +148,7 @@ describe('Component vnCrudModel', () => {
controller.moreRows = true;
- controller.loadMore();
+ controller.loadMore(true);
expect(controller.sendRequest).toHaveBeenCalledWith({'skip': 2}, true);
});
diff --git a/front/core/components/drop-down/index.js b/front/core/components/drop-down/index.js
index 08d0da6d0..302c1c6b5 100644
--- a/front/core/components/drop-down/index.js
+++ b/front/core/components/drop-down/index.js
@@ -212,12 +212,12 @@ export default class DropDown extends Popover {
&& !this.model.isLoading;
if (shouldLoad)
- this.model.loadMore();
+ this.model.loadMore(true);
}
onLoadMoreClick(event) {
if (event.defaultPrevented) return;
- this.model.loadMore();
+ this.model.loadMore(true);
}
onContainerClick(event) {
diff --git a/front/core/components/model-proxy/model-proxy.js b/front/core/components/model-proxy/model-proxy.js
index 26c28c803..0b8d7ebf9 100644
--- a/front/core/components/model-proxy/model-proxy.js
+++ b/front/core/components/model-proxy/model-proxy.js
@@ -374,9 +374,10 @@ export class Paginable {
/**
* When limit is enabled, loads the next set of rows.
*
+ * @param {Boolean} append - Whether should append new data
* @return {Promise} The request promise
*/
- loadMore() {
- return Promise.resolve();
+ loadMore(append) {
+ return Promise.resolve(append);
}
}
diff --git a/front/core/components/pagination/pagination.js b/front/core/components/pagination/pagination.js
index 9979be368..e7127734c 100644
--- a/front/core/components/pagination/pagination.js
+++ b/front/core/components/pagination/pagination.js
@@ -73,7 +73,7 @@ class Pagination extends Component {
if (shouldLoad) {
this.nLoads++;
- this.model.loadMore();
+ this.model.loadMore(false);
this.$.$apply();
}
}
@@ -82,7 +82,7 @@ class Pagination extends Component {
if (this.maxLoads > 0 && this.nLoads == this.maxLoads)
this.nLoads = 0;
- this.model.loadMore();
+ this.model.loadMore(false);
}
$onDestroy() {
diff --git a/front/package-lock.json b/front/package-lock.json
index 6922cae53..256797df0 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -3,6 +3,13 @@
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
+<<<<<<< HEAD
+ "dependencies": {
+ "@uirouter/angularjs": {
+ "version": "1.0.29",
+ "resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.29.tgz",
+ "integrity": "sha512-RImWnBarNixkMto0o8stEaGwZmvhv5cnuOLXyMU2pY8MP2rgEF74ZNJTLeJCW14LR7XDUxVH8Mk8bPI6lxedmQ==",
+=======
"packages": {
"": {
"name": "salix-front",
@@ -174,11 +181,22 @@
"version": "1.0.30",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
+>>>>>>> dev
"requires": {
"@uirouter/core": "6.0.8"
}
},
"@uirouter/core": {
+<<<<<<< HEAD
+ "version": "6.0.7",
+ "resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.7.tgz",
+ "integrity": "sha512-KUTJxL+6q0PiBnFx4/Z+Hsyg0pSGiaW5yZQeJmUxknecjpTbnXkLU8H2EqRn9N2B+qDRa7Jg8RcgeNDPY72O1w=="
+ },
+ "angular": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.2.tgz",
+ "integrity": "sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw=="
+=======
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw=="
@@ -187,6 +205,7 @@
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
+>>>>>>> dev
},
"angular-animate": {
"version": "1.8.2",
@@ -202,17 +221,29 @@
}
},
"angular-translate": {
+<<<<<<< HEAD
+ "version": "2.18.4",
+ "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz",
+ "integrity": "sha512-KohNrkH6J9PK+VW0L/nsRTcg5Fw70Ajwwe3Jbfm54Pf9u9Fd+wuingoKv+h45mKf38eT+Ouu51FPua8VmZNoCw==",
+=======
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
+>>>>>>> dev
"requires": {
"angular": "^1.8.0"
}
},
"angular-translate-loader-partial": {
+<<<<<<< HEAD
+ "version": "2.18.4",
+ "resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.18.4.tgz",
+ "integrity": "sha512-bsjR+FbB0sdA2528E/ugwKdlPPQhA1looxLxI3otayBTFXBpED33besfSZhYAISLgNMSL038vSssfRUen9qD8w==",
+=======
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
+>>>>>>> dev
"requires": {
"angular-translate": "~2.19.0"
}
@@ -253,9 +284,15 @@
}
},
"moment": {
+<<<<<<< HEAD
+ "version": "2.29.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
+ "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
+=======
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
+>>>>>>> dev
},
"oclazyload": {
"version": "0.6.3",
diff --git a/front/salix/components/log/index.html b/front/salix/components/log/index.html
index 0a0449038..79dfcef8c 100644
--- a/front/salix/components/log/index.html
+++ b/front/salix/components/log/index.html
@@ -1,10 +1,12 @@
-
@@ -13,81 +15,53 @@
Date
- Author
- Model
- Action
- Name
- Before
- After
+ User
+ Model
+ Action
+ Name
+ Changes
{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}
-
-
- Changed by:
- {{::log.user.name || 'System' | translate}}
-
-
-
- Model:
- {{::log.changedModel | dashIfEmpty}}
-
-
- Action:
- {{::$ctrl.actionsText[log.action] | dashIfEmpty}}
-
-
- Name:
- {{::log.changedModelValue | dashIfEmpty}}
-
-
-
+
{{::log.user.name || 'System' | translate}}
-
+
{{::log.changedModel}}
-
+
{{::$ctrl.actionsText[log.action]}}
-
+
{{::log.changedModelValue}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{::log.description}}
-
-
+
+
+
+
+ Field |
+ Before |
+ After |
+
+
+
+
+ {{prop.name}} |
+ {{prop.old}} |
+ {{prop.new}} |
+
+
+
+
+ {{::log.description}}
+
@@ -96,4 +70,4 @@
-
\ No newline at end of file
+
diff --git a/front/salix/components/log/index.js b/front/salix/components/log/index.js
index f30878b9f..1c54aa9b8 100644
--- a/front/salix/components/log/index.js
+++ b/front/salix/components/log/index.js
@@ -2,15 +2,17 @@ import ngModule from '../../module';
import Section from '../section';
import './style.scss';
+const validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
+
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.actionsText = {
- 'insert': 'Creates',
- 'update': 'Updates',
- 'delete': 'Deletes',
- 'select': 'Views'
- }; ``;
+ insert: 'Creates',
+ update: 'Updates',
+ delete: 'Deletes',
+ select: 'Views'
+ };
this.filter = {
include: [{
relation: 'user',
@@ -33,32 +35,57 @@ export default class Controller extends Section {
set logs(value) {
this._logs = value;
- if (!value) return;
-
+ if (!this.logs) return;
+ const empty = {};
const validations = window.validations;
- value.forEach(log => {
- const locale = validations[log.changedModel] && validations[log.changedModel].locale ? validations[log.changedModel].locale : {};
+ for (const log of value) {
+ const oldValues = log.oldInstance || empty;
+ const newValues = log.newInstance || empty;
+ const locale = validations[log.changedModel]?.locale || empty;
- log.oldProperties = this.getInstance(log.oldInstance, locale);
- log.newProperties = this.getInstance(log.newInstance, locale);
- });
+ let props = Object.keys(oldValues).concat(Object.keys(newValues));
+ props = [...new Set(props)];
+
+ log.props = [];
+ for (const prop of props) {
+ log.props.push({
+ name: locale[prop] || prop,
+ old: this.formatValue(oldValues[prop]),
+ new: this.formatValue(newValues[prop])
+ });
+ }
+ }
}
- getInstance(instance, locale) {
- const properties = [];
- let validDate = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
+ get showModelName() {
+ return !(this.changedModel && this.changedModelId);
+ }
- if (typeof instance == 'object' && instance != null) {
- Object.keys(instance).forEach(property => {
- if (validDate.test(instance[property]))
- instance[property] = new Date(instance[property]).toLocaleString('es-ES');
+ formatValue(value) {
+ let type = typeof value;
- const key = locale[property] || property;
- properties.push({key, value: instance[property]});
- });
- return properties;
+ if (type === 'string' && validDate.test(value)) {
+ value = new Date(value);
+ type = typeof value;
+ }
+
+ switch (type) {
+ case 'boolean':
+ return value ? '✓' : '✗';
+ case 'object':
+ if (value instanceof Date) {
+ const hasZeroTime =
+ value.getHours() === 0 &&
+ value.getMinutes() === 0 &&
+ value.getSeconds() === 0;
+ const format = hasZeroTime ? 'dd/MM/yyyy' : 'dd/MM/yyyy HH:mm:ss';
+ return this.$filter('date')(value, format);
+ }
+ else
+ return value;
+ default:
+ return value;
}
- return null;
}
showWorkerDescriptor(event, workerId) {
@@ -73,6 +100,8 @@ ngModule.vnComponent('vnLog', {
bindings: {
model: '<',
originId: '<',
+ changedModel: '',
+ changedModelId: '',
url: '@'
}
});
diff --git a/front/salix/components/log/locale/es.yml b/front/salix/components/log/locale/es.yml
index f483bea5e..d341095d8 100644
--- a/front/salix/components/log/locale/es.yml
+++ b/front/salix/components/log/locale/es.yml
@@ -11,4 +11,5 @@ Updates: Actualiza
Deletes: Elimina
Views: Visualiza
System: Sistema
-note: nota
\ No newline at end of file
+note: nota
+Changes: Cambios
diff --git a/front/salix/components/log/style.scss b/front/salix/components/log/style.scss
index ca1de113a..bc943996d 100644
--- a/front/salix/components/log/style.scss
+++ b/front/salix/components/log/style.scss
@@ -23,6 +23,28 @@ vn-log {
display: block;
}
}
+ .attributes {
+ width: 100%;
+
+ tr {
+ height: 10px;
+
+ & > td {
+ padding: 2px;
+ }
+ & > td.field,
+ & > th.field {
+ width: 20%;
+ color: gray;
+ }
+ & > td.before,
+ & > th.before,
+ & > td.after,
+ & > th.after {
+ width: 40%;
+ }
+ }
+ }
}
.ellipsis {
white-space: nowrap;
@@ -40,4 +62,4 @@ vn-log {
.alignSpan {
overflow: hidden;
display: inline-block;
-}
\ No newline at end of file
+}
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index 4885e34ec..a406b55a5 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -143,6 +143,7 @@
"Email verify": "Email verify",
"Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) merged with [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})",
"Sale(s) blocked, please contact production": "Sale(s) blocked, please contact production",
+ "App locked": "App locked by user {{userId}}",
"The sales of the receiver ticket can't be modified": "The sales of the receiver ticket can't be modified",
"Receipt's bank was not found": "Receipt's bank was not found",
"This receipt was not compensated": "This receipt was not compensated",
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index a500ff550..ea83b36c4 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -246,9 +246,11 @@
"Already has this status": "Ya tiene este estado",
"There aren't records for this week": "No existen registros para esta semana",
"Empty data source": "Origen de datos vacio",
+ "App locked": "Aplicación bloqueada por el usuario {{userId}}",
"Email verify": "Correo de verificación",
"Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment",
- "Receipt's bank was not found": "No se encontró el banco del recibo",
- "This receipt was not compensated": "Este recibo no ha sido compensado",
- "Client's email was not found": "No se encontró el email del cliente"
-}
+ "Receipt's bank was not found": "No se encontró el banco del recibo",
+ "This receipt was not compensated": "Este recibo no ha sido compensado",
+ "Client's email was not found": "No se encontró el email del cliente",
+ "Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9"
+}
\ No newline at end of file
diff --git a/modules/mdb/back/methods/mdbApp/lock.js b/modules/mdb/back/methods/mdbApp/lock.js
new file mode 100644
index 000000000..98e61fb53
--- /dev/null
+++ b/modules/mdb/back/methods/mdbApp/lock.js
@@ -0,0 +1,66 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('lock', {
+ description: 'Lock an app for the user',
+ accessType: 'WRITE',
+ accepts: [
+ {
+ arg: 'appName',
+ type: 'string',
+ required: true,
+ description: 'The app name',
+ http: {source: 'path'}
+
+ }
+ ],
+ returns: {
+ type: ['object'],
+ root: true
+ },
+ http: {
+ path: `/:appName/lock`,
+ verb: 'POST'
+ }
+ });
+
+ Self.lock = async(ctx, appName, options) => {
+ const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
+ const myOptions = {};
+ const $t = ctx.req.__; // $translate
+
+ let tx;
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ const mdbApp = await models.MdbApp.findById(appName, {fields: ['app', 'locked', 'userFk']}, myOptions);
+
+ if (mdbApp.locked) {
+ throw new UserError($t('App locked', {
+ userId: mdbApp.userFk
+ }));
+ }
+
+ const updatedMdbApp = await mdbApp.updateAttributes({
+ userFk: userId,
+ locked: new Date()
+ }, myOptions);
+
+ if (tx) await tx.commit();
+
+ return updatedMdbApp;
+ } catch (e) {
+ if (tx) await tx.rollback();
+
+ throw e;
+ }
+ };
+};
diff --git a/modules/mdb/back/methods/mdbApp/specs/lock.spec.js b/modules/mdb/back/methods/mdbApp/specs/lock.spec.js
new file mode 100644
index 000000000..162d0490a
--- /dev/null
+++ b/modules/mdb/back/methods/mdbApp/specs/lock.spec.js
@@ -0,0 +1,51 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('MdbApp lock()', () => {
+ it('should throw an error if the app is already locked', async() => {
+ const tx = await models.MdbApp.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx};
+ const appName = 'bar';
+ const developerId = 9;
+ const ctx = {
+ req: {
+ accessToken: {userId: developerId},
+ __: () => {}
+ }
+ };
+
+ const result = await models.MdbApp.lock(ctx, appName, options);
+
+ expect(result.locked).not.toBeNull();
+
+ await tx.rollback();
+ } catch (e) {
+ error = e;
+
+ await tx.rollback();
+ }
+
+ expect(error).toBeDefined();
+ });
+
+ it(`should lock a mdb `, async() => {
+ const tx = await models.MdbApp.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+ const appName = 'foo';
+ const developerId = 9;
+ const ctx = {req: {accessToken: {userId: developerId}}};
+
+ const result = await models.MdbApp.lock(ctx, appName, options);
+
+ expect(result.locked).not.toBeNull();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ }
+ });
+});
diff --git a/modules/mdb/back/methods/mdbApp/specs/unlock.spec.js b/modules/mdb/back/methods/mdbApp/specs/unlock.spec.js
new file mode 100644
index 000000000..9f1678372
--- /dev/null
+++ b/modules/mdb/back/methods/mdbApp/specs/unlock.spec.js
@@ -0,0 +1,22 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('MdbApp unlock()', () => {
+ it(`should unlock a mdb `, async() => {
+ const tx = await models.MdbApp.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+ const appName = 'bar';
+ const developerId = 9;
+ const ctx = {req: {accessToken: {userId: developerId}}};
+
+ const result = await models.MdbApp.unlock(ctx, appName, options);
+
+ expect(result.locked).toBeNull();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ }
+ });
+});
diff --git a/modules/mdb/back/methods/mdbApp/unlock.js b/modules/mdb/back/methods/mdbApp/unlock.js
new file mode 100644
index 000000000..6bf67ddf4
--- /dev/null
+++ b/modules/mdb/back/methods/mdbApp/unlock.js
@@ -0,0 +1,40 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('unlock', {
+ description: 'Unlock an app for the user',
+ accessType: 'WRITE',
+ accepts: [
+ {
+ arg: 'appName',
+ type: 'string',
+ required: true,
+ description: 'The app name',
+ http: {source: 'path'}
+
+ }
+ ],
+ returns: {
+ type: ['object'],
+ root: true
+ },
+ http: {
+ path: `/:appName/unlock`,
+ verb: 'POST'
+ }
+ });
+
+ Self.unlock = async(ctx, appName, options) => {
+ const models = Self.app.models;
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const mdbApp = await models.MdbApp.findById(appName, null, myOptions);
+ const updatedMdbApp = await mdbApp.updateAttributes({
+ userFk: null,
+ locked: null
+ }, myOptions);
+
+ return updatedMdbApp;
+ };
+};
diff --git a/modules/mdb/back/methods/mdbVersion/upload.js b/modules/mdb/back/methods/mdbVersion/upload.js
index 57df35ce7..5dfe5d3ef 100644
--- a/modules/mdb/back/methods/mdbVersion/upload.js
+++ b/modules/mdb/back/methods/mdbVersion/upload.js
@@ -23,6 +23,12 @@ module.exports = Self => {
type: 'string',
required: true,
description: `The branch name`
+ },
+ {
+ arg: 'unlock',
+ type: 'boolean',
+ required: false,
+ description: `It allows unlock the app`
}
],
returns: {
@@ -35,9 +41,11 @@ module.exports = Self => {
}
});
- Self.upload = async(ctx, appName, newVersion, branch, options) => {
+ Self.upload = async(ctx, appName, newVersion, branch, unlock, options) => {
const models = Self.app.models;
+ const userId = ctx.req.accessToken.userId;
const myOptions = {};
+ const $t = ctx.req.__; // $translate
const TempContainer = models.TempContainer;
const AccessContainer = models.AccessContainer;
@@ -55,6 +63,14 @@ module.exports = Self => {
let srcFile;
try {
+ const mdbApp = await models.MdbApp.findById(appName, null, myOptions);
+
+ if (mdbApp.locked && mdbApp.userFk != userId) {
+ throw new UserError($t('App locked', {
+ userId: mdbApp.userFk
+ }));
+ }
+
const tempContainer = await TempContainer.container('access');
const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions);
const files = Object.values(uploaded.files).map(file => {
@@ -79,7 +95,7 @@ module.exports = Self => {
const existBranch = await models.MdbBranch.findOne({
where: {name: branch}
- });
+ }, myOptions);
if (!existBranch)
throw new UserError('Not exist this branch');
@@ -108,7 +124,9 @@ module.exports = Self => {
app: appName,
branchFk: branch,
version: newVersion
- });
+ }, myOptions);
+
+ if (unlock) await models.MdbApp.unlock(ctx, appName, myOptions);
if (tx) await tx.commit();
} catch (e) {
diff --git a/modules/mdb/back/model-config.json b/modules/mdb/back/model-config.json
index d5be8de87..6107f8790 100644
--- a/modules/mdb/back/model-config.json
+++ b/modules/mdb/back/model-config.json
@@ -1,4 +1,7 @@
{
+ "MdbApp": {
+ "dataSource": "vn"
+ },
"MdbBranch": {
"dataSource": "vn"
},
diff --git a/modules/mdb/back/models/mdbApp.js b/modules/mdb/back/models/mdbApp.js
new file mode 100644
index 000000000..dce715573
--- /dev/null
+++ b/modules/mdb/back/models/mdbApp.js
@@ -0,0 +1,4 @@
+module.exports = Self => {
+ require('../methods/mdbApp/lock')(Self);
+ require('../methods/mdbApp/unlock')(Self);
+};
diff --git a/modules/mdb/back/models/mdbApp.json b/modules/mdb/back/models/mdbApp.json
new file mode 100644
index 000000000..868f8c1d0
--- /dev/null
+++ b/modules/mdb/back/models/mdbApp.json
@@ -0,0 +1,31 @@
+{
+ "name": "MdbApp",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "mdbApp"
+ }
+ },
+ "properties": {
+ "app": {
+ "type": "string",
+ "description": "The app name",
+ "id": true
+ },
+ "locked": {
+ "type": "date"
+ }
+ },
+ "relations": {
+ "branch": {
+ "type": "belongsTo",
+ "model": "MdbBranch",
+ "foreignKey": "baselineBranchFk"
+ },
+ "user": {
+ "type": "belongsTo",
+ "model": "MdbBranch",
+ "foreignKey": "userFk"
+ }
+ }
+}
diff --git a/modules/monitor/back/methods/sales-monitor/clientsFilter.js b/modules/monitor/back/methods/sales-monitor/clientsFilter.js
index 3756a706b..09ea24eb1 100644
--- a/modules/monitor/back/methods/sales-monitor/clientsFilter.js
+++ b/modules/monitor/back/methods/sales-monitor/clientsFilter.js
@@ -38,6 +38,7 @@ module.exports = Self => {
date.setHours(0, 0, 0, 0);
const stmt = new ParameterizedSQL(`
SELECT
+ v.id,
u.name AS salesPerson,
IFNULL(sc.workerSubstitute, c.salesPersonFk) AS salesPersonFk,
c.id AS clientFk,
diff --git a/modules/monitor/front/index/clients/index.html b/modules/monitor/front/index/clients/index.html
index 381c0e1ae..c0e3d1b14 100644
--- a/modules/monitor/front/index/clients/index.html
+++ b/modules/monitor/front/index/clients/index.html
@@ -61,7 +61,7 @@
-
+
{{::visit.dated | date:'dd/MM/yy'}}
diff --git a/modules/monitor/front/index/orders/index.html b/modules/monitor/front/index/orders/index.html
index 6126e23ef..74e80e40e 100644
--- a/modules/monitor/front/index/orders/index.html
+++ b/modules/monitor/front/index/orders/index.html
@@ -41,7 +41,7 @@
SalesPerson
-
diff --git a/modules/monitor/front/index/tickets/index.html b/modules/monitor/front/index/tickets/index.html
index 138788ed6..2f7c34e2d 100644
--- a/modules/monitor/front/index/tickets/index.html
+++ b/modules/monitor/front/index/tickets/index.html
@@ -78,7 +78,7 @@
|
-
+
+
diff --git a/print/templates/email/book-entries-imported-incorrectly/book-entries-imported-incorrectly.js b/print/templates/email/book-entries-imported-incorrectly/book-entries-imported-incorrectly.js
new file mode 100755
index 000000000..c8a427d30
--- /dev/null
+++ b/print/templates/email/book-entries-imported-incorrectly/book-entries-imported-incorrectly.js
@@ -0,0 +1,15 @@
+const Component = require(`vn-print/core/component`);
+const emailBody = new Component('email-body');
+
+module.exports = {
+ name: 'book-entries-imported-incorrectly',
+ components: {
+ 'email-body': emailBody.build(),
+ },
+ props: {
+ bookEntries: {
+ type: String,
+ required: true
+ }
+ }
+};
diff --git a/print/templates/email/book-entries-imported-incorrectly/locale/en.yml b/print/templates/email/book-entries-imported-incorrectly/locale/en.yml
new file mode 100644
index 000000000..30c5dd292
--- /dev/null
+++ b/print/templates/email/book-entries-imported-incorrectly/locale/en.yml
@@ -0,0 +1,5 @@
+subject: Book entries imported incorrectly
+title: Book entries imported incorrectly
+description: There are book entries that differ between the info from XDiario and the one that has been imported into Sage.
+ Book entries nº {0}
+ If you consider that it is due to a computer error, forward this email to cau@verdnatura.es
diff --git a/print/templates/email/book-entries-imported-incorrectly/locale/es.yml b/print/templates/email/book-entries-imported-incorrectly/locale/es.yml
new file mode 100644
index 000000000..f48ad0a3b
--- /dev/null
+++ b/print/templates/email/book-entries-imported-incorrectly/locale/es.yml
@@ -0,0 +1,5 @@
+subject: Asientos contables importados incorrectamente
+title: Asientos contables importados incorrectamente
+description: Existen asientos que difieren entre la info. de XDiario y la que se ha importado a Sage.
+ Asientos nº {0}
+ Si considera que es debido a un error informático, reenvíe este correo a cau@verdnatura.es
diff --git a/print/templates/reports/expedition-pallet-label/assets/css/style.css b/print/templates/reports/expedition-pallet-label/assets/css/style.css
index 68e91fb47..bd7366486 100644
--- a/print/templates/reports/expedition-pallet-label/assets/css/style.css
+++ b/print/templates/reports/expedition-pallet-label/assets/css/style.css
@@ -17,7 +17,6 @@ html {
}
.mainTable {
width: 100%;
- height: 100%;
border: 10px solid;
border-radius: 20px;
-moz-border-radius: 20px;
@@ -54,8 +53,17 @@ html {
}
#QR {
padding: 25px;
- padding-left: 40px;
+ margin-left: 35px;
+ float:left;
+}
+#barcode{
+ text-align: center;
+}
+#right {
+ float: right;
margin-top: 20px;
+ width: 250;
+ max-width: 250px;
}
#additionalInfo {
padding-top: 20px;
diff --git a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html
index 45c6ab463..e4360c79d 100644
--- a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html
+++ b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html
@@ -4,10 +4,11 @@
- {{labelData.truck || '---'}} |
+ {{labelData.truck || '---'}} |
-
+ |
+
@@ -25,11 +26,13 @@
- |
-
- Pallet: {{id}}
- User: {{username.name || '---'}}
- Day: {{labelData.dayName.toUpperCase() || '---'}}
+ |
+
+
+ Pallet: {{id}}
+ User: {{username.name || '---'}}
+ Day: {{labelData.dayName.toUpperCase() || '---'}}
+
|
diff --git a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js
index 3613da08f..0e8c297e8 100644
--- a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js
+++ b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js
@@ -1,5 +1,7 @@
const Component = require(`vn-print/core/component`);
const reportBody = new Component('report-body');
+const jsBarcode = require('jsbarcode');
+const {DOMImplementation, XMLSerializer} = require('xmldom');
const UserError = require('vn-loopback/util/user-error');
const qrcode = require('qrcode');
@@ -39,6 +41,20 @@ module.exports = {
const data = String(id);
return qrcode.toDataURL(data, {margin: 0});
},
+ getBarcode(id) {
+ const xmlSerializer = new XMLSerializer();
+ const document = new DOMImplementation().createDocument('http://www.w3.org/1999/xhtml', 'html', null);
+ const svgNode = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+
+ jsBarcode(svgNode, id, {
+ xmlDocument: document,
+ format: 'code128',
+ displayValue: false,
+ width: 6,
+ height: 90,
+ });
+ return xmlSerializer.serializeToString(svgNode);
+ },
},
components: {
'report-body': reportBody.build()
diff --git a/print/templates/reports/expedition-pallet-label/options.json b/print/templates/reports/expedition-pallet-label/options.json
index 269e058f0..5814b2c0f 100644
--- a/print/templates/reports/expedition-pallet-label/options.json
+++ b/print/templates/reports/expedition-pallet-label/options.json
@@ -2,8 +2,8 @@
"width": "10cm",
"height": "15cm",
"margin": {
- "top": "0.5cm",
- "right": "0.2cm",
+ "top": "0.3cm",
+ "right": "0.07cm",
"bottom": "0cm",
"left": "0cm"
},
|