Merge branch 'dev' into #288-traducir-state-i18n
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Pau 2022-12-23 08:09:38 +00:00
commit fc86e913c4
50 changed files with 1428 additions and 157 deletions

View File

@ -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;
}
};
};

View File

@ -6,6 +6,7 @@ module.exports = Self => {
require('../methods/dms/removeFile')(Self); require('../methods/dms/removeFile')(Self);
require('../methods/dms/updateFile')(Self); require('../methods/dms/updateFile')(Self);
require('../methods/dms/deleteTrashFiles')(Self); require('../methods/dms/deleteTrashFiles')(Self);
require('../methods/dms/saveSign')(Self);
Self.checkRole = async function(ctx, id) { Self.checkRole = async function(ctx, id) {
const models = Self.app.models; const models = Self.app.models;

View File

@ -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

View File

@ -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');

View File

@ -0,0 +1 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`) VALUES ('Dms','saveSign','*','ALLOW','employee');

View File

@ -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 ;

View File

@ -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 ;

View File

@ -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);

View File

@ -1335,9 +1335,9 @@ INSERT INTO `vn`.`itemTypeTag`(`id`, `itemTypeFk`, `tagFk`, `priority`)
CALL `vn`.`itemRefreshTags`(NULL); 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 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`) INSERT INTO `vn`.`recovery`(`id`, `clientFk`, `started`, `finished`, `amount`, `period`)
VALUES VALUES
@ -2692,7 +2692,7 @@ INSERT INTO `util`.`notificationConfig`
INSERT INTO `util`.`notification` (`id`, `name`, `description`) INSERT INTO `util`.`notification` (`id`, `name`, `description`)
VALUES VALUES
(1, 'print-email', 'notification fixture one'), (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`) INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
VALUES VALUES
@ -2707,7 +2707,8 @@ INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `autho
INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`) INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`)
VALUES VALUES
(1, 1109), (1, 1109),
(1, 1110); (1, 1110),
(3, 1109);
INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`) INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`)
VALUES VALUES
@ -2745,10 +2746,15 @@ INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`
VALUES 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'); (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`) INSERT INTO `vn`.`ticketLog` (`id`, `originFk`, `userFk`, `action`, `changedModel`, `oldInstance`, `newInstance`, `changedModelId`)
VALUES VALUES
(1, 1, 9, 'insert', 'Ticket', '{}', '{"clientFk":1, "nickname": "Bat cave"}', 1); (1, 1, 9, 'insert', 'Ticket', '{}', '{"clientFk":1, "nickname": "Bat cave"}', 1);
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`) INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
VALUES VALUES
('lilium', 'dev', 'http://localhost:8080/#/'), ('lilium', 'dev', 'http://localhost:8080/#/'),

View File

@ -16340,6 +16340,185 @@ CREATE TABLE `invoiceType` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
/*!40101 SET character_set_client = @saved_cs_client */; /*!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` -- Table structure for table `movContaIVA`
-- --

View File

@ -84,7 +84,6 @@ IGNORETABLES=(
--ignore-table=vn.warehouseJoined --ignore-table=vn.warehouseJoined
--ignore-table=vn.workerTeam__ --ignore-table=vn.workerTeam__
--ignore-table=vn.XDiario__ --ignore-table=vn.XDiario__
--ignore-table=sage.movConta
--ignore-table=sage.movContaCopia --ignore-table=sage.movContaCopia
) )
mysqldump \ mysqldump \

View File

@ -311,10 +311,12 @@ export default {
firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr' firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr'
}, },
clientLog: { clientLog: {
lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before', lastModificationPreviousValue: 'vn-client-log vn-tr table tr td.before',
lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after', lastModificationCurrentValue: 'vn-client-log vn-tr table tr td.after',
penultimateModificationPreviousValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.before', namePreviousValue: 'vn-client-log vn-tr table tr:nth-child(1) td.before',
penultimateModificationCurrentValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.after' 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: { clientBalance: {
@ -518,7 +520,7 @@ export default {
}, },
itemLog: { itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr', 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: { ticketSummary: {
header: 'vn-ticket-summary > vn-card > h5', header: 'vn-ticket-summary > vn-card > h5',
@ -711,9 +713,10 @@ export default {
ticketLog: { ticketLog: {
firstTD: 'vn-ticket-log vn-table vn-td:nth-child(1)', firstTD: 'vn-ticket-log vn-table vn-td:nth-child(1)',
logButton: 'vn-left-menu a[ui-sref="ticket.card.log"]', logButton: 'vn-left-menu a[ui-sref="ticket.card.log"]',
firstLogEntry: 'vn-ticket-log vn-data-viewer vn-tbody vn-tr', user: 'vn-ticket-log vn-tbody vn-tr vn-td:nth-child(2)',
changes: 'vn-ticket-log vn-data-viewer vn-tbody > vn-tr > vn-td:nth-child(7)', action: 'vn-ticket-log vn-tbody vn-tr vn-td:nth-child(4)',
id: 'vn-ticket-log vn-tr:nth-child(1) vn-one:nth-child(1) span' 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: { ticketService: {
addServiceButton: 'vn-ticket-service vn-icon-button[vn-tooltip="Add service"] > button', 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"]' undoChanges: 'vn-travel-basic-data vn-button[label="Undo changes"]'
}, },
travelLog: { 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: { travelThermograph: {
add: 'vn-travel-thermograph-index vn-float-button[icon="add"]', add: 'vn-travel-thermograph-index vn-float-button[icon="add"]',

View File

@ -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() => { it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => {
let lastModificationPreviousValue = await page let namePreviousValue = await page
.waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText'); .waitToGetProperty(selectors.clientLog.namePreviousValue, 'innerText');
let lastModificationCurrentValue = await page let nameCurrentValue = await page
.waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText'); .waitToGetProperty(selectors.clientLog.nameCurrentValue, 'innerText');
expect(lastModificationPreviousValue).toEqual('name MaxEisenhardt active false'); expect(namePreviousValue).toEqual('MaxEisenhardt');
expect(lastModificationCurrentValue).toEqual('name Legion active false'); expect(nameCurrentValue).toEqual('Legion');
}); });
it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => { it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => {
let penultimateModificationPreviousValue = await page let activePreviousValue = await page
.waitToGetProperty(selectors.clientLog.penultimateModificationPreviousValue, 'innerText'); .waitToGetProperty(selectors.clientLog.activePreviousValue, 'innerText');
let penultimateModificationCurrentValue = await page let activeCurrentValue = await page
.waitToGetProperty(selectors.clientLog.penultimateModificationCurrentValue, 'innerText'); .waitToGetProperty(selectors.clientLog.activeCurrentValue, 'innerText');
expect(penultimateModificationPreviousValue).toEqual('name MaxEisenhardt active true'); expect(activePreviousValue).toEqual('✓');
expect(penultimateModificationCurrentValue).toEqual('name MaxEisenhardt active false'); expect(activeCurrentValue).toEqual('✗');
}); });
}); });

View File

@ -43,7 +43,7 @@ describe('Client log path', () => {
let lastModificationCurrentValue = await page. let lastModificationCurrentValue = await page.
waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText'); waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
expect(lastModificationPreviousValue).toEqual('name DavidCharlesHaller'); expect(lastModificationPreviousValue).toEqual('DavidCharlesHaller');
expect(lastModificationCurrentValue).toEqual('name this is a test'); expect(lastModificationCurrentValue).toEqual('this is a test');
}); });
}); });

View File

@ -32,14 +32,17 @@ describe('Ticket expeditions and log path', () => {
it(`should confirm the expedition deleted is shown now in the ticket log`, async() => { it(`should confirm the expedition deleted is shown now in the ticket log`, async() => {
await page.accessToSection('ticket.card.log'); await page.accessToSection('ticket.card.log');
const firstLogEntry = await page const user = await page
.waitToGetProperty(selectors.ticketLog.firstLogEntry, 'innerText'); .waitToGetProperty(selectors.ticketLog.user, 'innerText');
const action = await page
.waitToGetProperty(selectors.ticketLog.action, 'innerText');
const id = await page const id = await page
.waitToGetProperty(selectors.ticketLog.id, 'innerText'); .waitToGetProperty(selectors.ticketLog.id, 'innerText');
expect(firstLogEntry).toContain('production'); expect(user).toContain('production');
expect(firstLogEntry).toContain('Deletes'); expect(action).toContain('Deletes');
expect(id).toEqual('2'); expect(id).toEqual('2');
}); });
}); });

View File

@ -55,6 +55,6 @@ describe('Ticket log path', () => {
const result = await page.waitToGetProperty(selectors.ticketLog.firstTD, 'innerText'); const result = await page.waitToGetProperty(selectors.ticketLog.firstTD, 'innerText');
expect(result.length).toBeGreaterThan('20'); expect(result.length).toBeGreaterThan('15');
}); });
}); });

View File

@ -70,8 +70,8 @@ describe('Supplier basic data path', () => {
}); });
it('should check the changes have been recorded', async() => { 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');
}); });
}); });

View File

@ -14,6 +14,7 @@ export default class CrudModel extends ModelProxy {
this.$q = $q; this.$q = $q;
this.primaryKey = 'id'; this.primaryKey = 'id';
this.autoLoad = false; this.autoLoad = false;
this.page = 1;
} }
$onInit() { $onInit() {
@ -125,13 +126,20 @@ export default class CrudModel extends ModelProxy {
} }
} }
loadMore() { loadMore(append) {
if (!this.moreRows) if (!this.moreRows)
return this.$q.resolve(); return this.$q.resolve();
let filter = Object.assign({}, this.currentFilter); const filter = Object.assign({}, this.currentFilter);
filter.skip = this.orgData ? this.orgData.length : 0; if (append)
return this.sendRequest(filter, true); 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() { clear() {

View File

@ -148,7 +148,7 @@ describe('Component vnCrudModel', () => {
controller.moreRows = true; controller.moreRows = true;
controller.loadMore(); controller.loadMore(true);
expect(controller.sendRequest).toHaveBeenCalledWith({'skip': 2}, true); expect(controller.sendRequest).toHaveBeenCalledWith({'skip': 2}, true);
}); });

View File

@ -212,12 +212,12 @@ export default class DropDown extends Popover {
&& !this.model.isLoading; && !this.model.isLoading;
if (shouldLoad) if (shouldLoad)
this.model.loadMore(); this.model.loadMore(true);
} }
onLoadMoreClick(event) { onLoadMoreClick(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
this.model.loadMore(); this.model.loadMore(true);
} }
onContainerClick(event) { onContainerClick(event) {

View File

@ -374,9 +374,10 @@ export class Paginable {
/** /**
* When limit is enabled, loads the next set of rows. * When limit is enabled, loads the next set of rows.
* *
* @param {Boolean} append - Whether should append new data
* @return {Promise} The request promise * @return {Promise} The request promise
*/ */
loadMore() { loadMore(append) {
return Promise.resolve(); return Promise.resolve(append);
} }
} }

View File

@ -73,7 +73,7 @@ class Pagination extends Component {
if (shouldLoad) { if (shouldLoad) {
this.nLoads++; this.nLoads++;
this.model.loadMore(); this.model.loadMore(false);
this.$.$apply(); this.$.$apply();
} }
} }
@ -82,7 +82,7 @@ class Pagination extends Component {
if (this.maxLoads > 0 && this.nLoads == this.maxLoads) if (this.maxLoads > 0 && this.nLoads == this.maxLoads)
this.nLoads = 0; this.nLoads = 0;
this.model.loadMore(); this.model.loadMore(false);
} }
$onDestroy() { $onDestroy() {

View File

@ -3,6 +3,13 @@
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "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": { "packages": {
"": { "": {
"name": "salix-front", "name": "salix-front",
@ -174,11 +181,22 @@
"version": "1.0.30", "version": "1.0.30",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz", "resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==", "integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
>>>>>>> dev
"requires": { "requires": {
"@uirouter/core": "6.0.8" "@uirouter/core": "6.0.8"
} }
}, },
"@uirouter/core": { "@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", "version": "6.0.8",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz", "resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw==" "integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw=="
@ -187,6 +205,7 @@
"version": "1.8.3", "version": "1.8.3",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz", "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==" "integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
>>>>>>> dev
}, },
"angular-animate": { "angular-animate": {
"version": "1.8.2", "version": "1.8.2",
@ -202,17 +221,29 @@
} }
}, },
"angular-translate": { "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", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz", "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==", "integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
>>>>>>> dev
"requires": { "requires": {
"angular": "^1.8.0" "angular": "^1.8.0"
} }
}, },
"angular-translate-loader-partial": { "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", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz", "resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==", "integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
>>>>>>> dev
"requires": { "requires": {
"angular-translate": "~2.19.0" "angular-translate": "~2.19.0"
} }
@ -253,9 +284,15 @@
} }
}, },
"moment": { "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", "version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
>>>>>>> dev
}, },
"oclazyload": { "oclazyload": {
"version": "0.6.3", "version": "0.6.3",

View File

@ -1,10 +1,12 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="{{$ctrl.url}}" url="{{$ctrl.url}}"
filter="$ctrl.filter" filter="$ctrl.filter"
link="{originFk: $ctrl.originId}" link="{originFk: $ctrl.originId}"
data="$ctrl.logs" where="{changedModel: $ctrl.changedModel,
limit="20" changedModelId: $ctrl.changedModelId}"
data="$ctrl.logs"
limit="20"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<vn-data-viewer model="model" class="vn-w-xl"> <vn-data-viewer model="model" class="vn-w-xl">
@ -13,81 +15,53 @@
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th field="creationDate">Date</vn-th> <vn-th field="creationDate">Date</vn-th>
<vn-th field="userFk" class="expendable" shrink>Author</vn-th> <vn-th field="userFk" shrink>User</vn-th>
<vn-th field="changedModel" class="expendable">Model</vn-th> <vn-th field="changedModel" ng-if="$ctrl.showModelName" shrink>Model</vn-th>
<vn-th field="action" class="expendable" shrink>Action</vn-th> <vn-th field="action" shrink>Action</vn-th>
<vn-th field="changedModelValue" class="expendable">Name</vn-th> <vn-th field="changedModelValue" ng-if="$ctrl.showModelName">Name</vn-th>
<vn-th expand>Before</vn-th> <vn-th expand>Changes</vn-th>
<vn-th expand>After</vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="log in $ctrl.logs"> <vn-tr ng-repeat="log in $ctrl.logs">
<vn-td shrink-datetime> <vn-td shrink-datetime>
{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}} {{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}
<div class="changes">
<div>
<span translate class="label">Changed by</span><span class="label">: </span>
<span ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
translate>{{::log.user.name || 'System' | translate}}
</span>
</div>
<div>
<span translate class="label">Model</span><span class="label">: </span>
<span translate class="value">{{::log.changedModel | dashIfEmpty}}</span>
</div>
<div>
<span translate class="label">Action</span><span class="label">: </span>
<span translate class="value">{{::$ctrl.actionsText[log.action] | dashIfEmpty}}</span>
</div>
<div>
<span translate class="label">Name</span><span class="label">: </span>
<span translate class="value">{{::log.changedModelValue | dashIfEmpty}}</span>
</div>
</div>
</vn-td> </vn-td>
<vn-td class="expendable"> <vn-td>
<span ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}" <span ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)" ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
translate>{{::log.user.name || 'System' | translate}} translate>{{::log.user.name || 'System' | translate}}
</span> </span>
</vn-td> </vn-td>
<vn-td class="expendable"> <vn-td ng-if="$ctrl.showModelName">
{{::log.changedModel}} {{::log.changedModel}}
</vn-td> </vn-td>
<vn-td translate class="expendable"> <vn-td shrink translate>
{{::$ctrl.actionsText[log.action]}} {{::$ctrl.actionsText[log.action]}}
</vn-td> </vn-td>
<vn-td class="expendable" expand> <vn-td ng-if="$ctrl.showModelName">
{{::log.changedModelValue}} {{::log.changedModelValue}}
</vn-td> </vn-td>
<vn-td expand class="before"> <vn-td expand>
<vn-one ng-repeat="old in log.oldProperties"> <table class="attributes">
<div> <thead>
<vn-label-value <tr>
no-ellipsize <th translate class="field">Field</th>
label="{{::old.key}}" <th translate>Before</th>
value="{{::old.value}}"> <th translate>After</th>
</vn-label-value> </tr>
</div> </thead>
</vn-one> <tbody>
</vn-td> <tr ng-repeat="prop in ::log.props">
<vn-td expand class="after"> <td class="field">{{prop.name}}</td>
<vn-one ng-repeat="new in log.newProperties" ng-if="!log.description" id="newInstance"> <td class="before">{{prop.old}}</td>
<div> <td class="after">{{prop.new}}</td>
<vn-label-value </tr>
no-ellipsize </tbody>
label="{{::new.key}}" </table>
value="{{::new.value}}"> <div ng-if="log.description != null">
</vn-label-value> {{::log.description}}
</div> </div>
</vn-one>
<vn-one ng-if="!log.newProperties" id="description">
<div>
<span no-ellipsize>{{::log.description}}</span>
</div>
</vn-one>
</vn-td> </vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
@ -96,4 +70,4 @@
</vn-card> </vn-card>
</vn-data-viewer> </vn-data-viewer>
<vn-worker-descriptor-popover vn-id="workerDescriptor"> <vn-worker-descriptor-popover vn-id="workerDescriptor">
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>

View File

@ -2,15 +2,17 @@ import ngModule from '../../module';
import Section from '../section'; import Section from '../section';
import './style.scss'; 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 { export default class Controller extends Section {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.actionsText = { this.actionsText = {
'insert': 'Creates', insert: 'Creates',
'update': 'Updates', update: 'Updates',
'delete': 'Deletes', delete: 'Deletes',
'select': 'Views' select: 'Views'
}; ``; };
this.filter = { this.filter = {
include: [{ include: [{
relation: 'user', relation: 'user',
@ -33,32 +35,57 @@ export default class Controller extends Section {
set logs(value) { set logs(value) {
this._logs = value; this._logs = value;
if (!value) return; if (!this.logs) return;
const empty = {};
const validations = window.validations; const validations = window.validations;
value.forEach(log => { for (const log of value) {
const locale = validations[log.changedModel] && validations[log.changedModel].locale ? validations[log.changedModel].locale : {}; const oldValues = log.oldInstance || empty;
const newValues = log.newInstance || empty;
const locale = validations[log.changedModel]?.locale || empty;
log.oldProperties = this.getInstance(log.oldInstance, locale); let props = Object.keys(oldValues).concat(Object.keys(newValues));
log.newProperties = this.getInstance(log.newInstance, locale); 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) { get showModelName() {
const properties = []; return !(this.changedModel && this.changedModelId);
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)?$/; }
if (typeof instance == 'object' && instance != null) { formatValue(value) {
Object.keys(instance).forEach(property => { let type = typeof value;
if (validDate.test(instance[property]))
instance[property] = new Date(instance[property]).toLocaleString('es-ES');
const key = locale[property] || property; if (type === 'string' && validDate.test(value)) {
properties.push({key, value: instance[property]}); value = new Date(value);
}); type = typeof value;
return properties; }
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) { showWorkerDescriptor(event, workerId) {
@ -73,6 +100,8 @@ ngModule.vnComponent('vnLog', {
bindings: { bindings: {
model: '<', model: '<',
originId: '<', originId: '<',
changedModel: '<?',
changedModelId: '<?',
url: '@' url: '@'
} }
}); });

View File

@ -11,4 +11,5 @@ Updates: Actualiza
Deletes: Elimina Deletes: Elimina
Views: Visualiza Views: Visualiza
System: Sistema System: Sistema
note: nota note: nota
Changes: Cambios

View File

@ -23,6 +23,28 @@ vn-log {
display: block; 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 { .ellipsis {
white-space: nowrap; white-space: nowrap;
@ -40,4 +62,4 @@ vn-log {
.alignSpan { .alignSpan {
overflow: hidden; overflow: hidden;
display: inline-block; display: inline-block;
} }

View File

@ -143,6 +143,7 @@
"Email verify": "Email verify", "Email verify": "Email verify",
"Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) merged with [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})", "Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) merged with [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})",
"Sale(s) blocked, please contact production": "Sale(s) blocked, please contact production", "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", "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", "Receipt's bank was not found": "Receipt's bank was not found",
"This receipt was not compensated": "This receipt was not compensated", "This receipt was not compensated": "This receipt was not compensated",

View File

@ -246,9 +246,11 @@
"Already has this status": "Ya tiene este estado", "Already has this status": "Ya tiene este estado",
"There aren't records for this week": "No existen registros para esta semana", "There aren't records for this week": "No existen registros para esta semana",
"Empty data source": "Origen de datos vacio", "Empty data source": "Origen de datos vacio",
"App locked": "Aplicación bloqueada por el usuario {{userId}}",
"Email verify": "Correo de verificación", "Email verify": "Correo de verificación",
"Landing cannot be lesser than shipment": "Landing cannot be lesser than shipment", "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", "Receipt's bank was not found": "No se encontró el banco del recibo",
"This receipt was not compensated": "Este recibo no ha sido compensado", "This receipt was not compensated": "Este recibo no ha sido compensado",
"Client's email was not found": "No se encontró el email del cliente" "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"
}

View File

@ -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;
}
};
};

View File

@ -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();
}
});
});

View File

@ -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();
}
});
});

View File

@ -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;
};
};

View File

@ -23,6 +23,12 @@ module.exports = Self => {
type: 'string', type: 'string',
required: true, required: true,
description: `The branch name` description: `The branch name`
},
{
arg: 'unlock',
type: 'boolean',
required: false,
description: `It allows unlock the app`
} }
], ],
returns: { 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 models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {}; const myOptions = {};
const $t = ctx.req.__; // $translate
const TempContainer = models.TempContainer; const TempContainer = models.TempContainer;
const AccessContainer = models.AccessContainer; const AccessContainer = models.AccessContainer;
@ -55,6 +63,14 @@ module.exports = Self => {
let srcFile; let srcFile;
try { 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 tempContainer = await TempContainer.container('access');
const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions); const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions);
const files = Object.values(uploaded.files).map(file => { const files = Object.values(uploaded.files).map(file => {
@ -79,7 +95,7 @@ module.exports = Self => {
const existBranch = await models.MdbBranch.findOne({ const existBranch = await models.MdbBranch.findOne({
where: {name: branch} where: {name: branch}
}); }, myOptions);
if (!existBranch) if (!existBranch)
throw new UserError('Not exist this branch'); throw new UserError('Not exist this branch');
@ -108,7 +124,9 @@ module.exports = Self => {
app: appName, app: appName,
branchFk: branch, branchFk: branch,
version: newVersion version: newVersion
}); }, myOptions);
if (unlock) await models.MdbApp.unlock(ctx, appName, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();
} catch (e) { } catch (e) {

View File

@ -1,4 +1,7 @@
{ {
"MdbApp": {
"dataSource": "vn"
},
"MdbBranch": { "MdbBranch": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,4 @@
module.exports = Self => {
require('../methods/mdbApp/lock')(Self);
require('../methods/mdbApp/unlock')(Self);
};

View File

@ -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"
}
}
}

View File

@ -38,6 +38,7 @@ module.exports = Self => {
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
const stmt = new ParameterizedSQL(` const stmt = new ParameterizedSQL(`
SELECT SELECT
v.id,
u.name AS salesPerson, u.name AS salesPerson,
IFNULL(sc.workerSubstitute, c.salesPersonFk) AS salesPersonFk, IFNULL(sc.workerSubstitute, c.salesPersonFk) AS salesPersonFk,
c.id AS clientFk, c.id AS clientFk,

View File

@ -61,7 +61,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="visit in model.data"> <tr ng-repeat="visit in model.data track by visit.id">
<td shrink-date> <td shrink-date>
<span class="chip"> <span class="chip">
{{::visit.dated | date:'dd/MM/yy'}} {{::visit.dated | date:'dd/MM/yy'}}

View File

@ -41,7 +41,7 @@
<vn-th field="salesPersonFk" shrink>SalesPerson</vn-th> <vn-th field="salesPersonFk" shrink>SalesPerson</vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<a ng-repeat="order in model.data" <a ng-repeat="order in model.data track by order.id"
class="clickable vn-tbody" class="clickable vn-tbody"
ui-sref="order.card.summary({id: {{::order.id}}})" target="_blank"> ui-sref="order.card.summary({id: {{::order.id}}})" target="_blank">
<vn-tr> <vn-tr>

View File

@ -78,7 +78,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="ticket in model.data" <tr ng-repeat="ticket in model.data track by ticket.id"
vn-anchor="::{ vn-anchor="::{
state: 'ticket.card.summary', state: 'ticket.card.summary',
params: {id: ticket.id}, params: {id: ticket.id},

View File

@ -0,0 +1,12 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/email.css`])
.mergeStyles();

View File

@ -0,0 +1,8 @@
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p v-html="$t('description', [bookEntries])"></p>
</div>
</div>
</email-body>

View File

@ -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
}
}
};

View File

@ -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.<br/><br/>
Book entries nº {0} <br/><br/>
If you consider that it is due to a computer error, forward this email to cau@verdnatura.es

View File

@ -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.<br/><br/>
Asientos nº {0} <br/><br/>
Si considera que es debido a un error informático, reenvíe este correo a cau@verdnatura.es

View File

@ -17,7 +17,6 @@ html {
} }
.mainTable { .mainTable {
width: 100%; width: 100%;
height: 100%;
border: 10px solid; border: 10px solid;
border-radius: 20px; border-radius: 20px;
-moz-border-radius: 20px; -moz-border-radius: 20px;
@ -54,8 +53,17 @@ html {
} }
#QR { #QR {
padding: 25px; padding: 25px;
padding-left: 40px; margin-left: 35px;
float:left;
}
#barcode{
text-align: center;
}
#right {
float: right;
margin-top: 20px; margin-top: 20px;
width: 250;
max-width: 250px;
} }
#additionalInfo { #additionalInfo {
padding-top: 20px; padding-top: 20px;

View File

@ -4,10 +4,11 @@
<table class="mainTable"> <table class="mainTable">
<tbody> <tbody>
<tr> <tr>
<td colspan="2" id="truck" class="ellipsize">{{labelData.truck || '---'}}</td> <td id="truck" class="ellipsize">{{labelData.truck || '---'}}</td>
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td>
<div v-html="getBarcode(labelData.palletFk)" id="barcode"></div>
<table v-for="labelData in labelsData" class="zoneTable"> <table v-for="labelData in labelsData" class="zoneTable">
<thead> <thead>
<tr v-if="!labelData.isMatch" id="black"> <tr v-if="!labelData.isMatch" id="black">
@ -25,11 +26,13 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td><img :src="QR" id="QR"/></td> <td>
<td class="ellipsize"> <img :src="QR" id="QR"/>
<div id="additionalInfo"><b>Pallet: </b>{{id}}</div> <div id="right">
<div id="additionalInfo"><b>User: </b> {{username.name || '---'}}</div> <div id="additionalInfo" class="ellipsize"><b>Pallet: </b>{{id}}</div>
<div id="additionalInfo"><b>Day: </b>{{labelData.dayName.toUpperCase() || '---'}}</div> <div id="additionalInfo" class="ellipsize"><b>User: </b> {{username.name || '---'}}</div>
<div id="additionalInfo" class="ellipsize"><b>Day: </b>{{labelData.dayName.toUpperCase() || '---'}}</div>
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -1,5 +1,7 @@
const Component = require(`vn-print/core/component`); const Component = require(`vn-print/core/component`);
const reportBody = new Component('report-body'); const reportBody = new Component('report-body');
const jsBarcode = require('jsbarcode');
const {DOMImplementation, XMLSerializer} = require('xmldom');
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
const qrcode = require('qrcode'); const qrcode = require('qrcode');
@ -39,6 +41,20 @@ module.exports = {
const data = String(id); const data = String(id);
return qrcode.toDataURL(data, {margin: 0}); 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: { components: {
'report-body': reportBody.build() 'report-body': reportBody.build()

View File

@ -2,8 +2,8 @@
"width": "10cm", "width": "10cm",
"height": "15cm", "height": "15cm",
"margin": { "margin": {
"top": "0.5cm", "top": "0.3cm",
"right": "0.2cm", "right": "0.07cm",
"bottom": "0cm", "bottom": "0cm",
"left": "0cm" "left": "0cm"
}, },