diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..bafedc760 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2302.01] - 2023-01-12 + +### Added +- [General](Inicio) Permite recuperar la contraseña + +### Changed + + +### Removed +- [Tickets](Control clientes) Eliminada sección diff --git a/back/methods/campaign/latest.js b/back/methods/campaign/latest.js index 20dda8a26..a418f1267 100644 --- a/back/methods/campaign/latest.js +++ b/back/methods/campaign/latest.js @@ -29,8 +29,12 @@ module.exports = Self => { filter = mergeFilters(filter, {where}); const stmt = new ParameterizedSQL( - `SELECT * FROM campaign`); + `SELECT * FROM (`); + stmt.merge('SELECT * FROM campaign'); stmt.merge(conn.makeWhere(filter.where)); + stmt.merge('ORDER BY dated ASC'); + stmt.merge('LIMIT 10000000000000000000'); + stmt.merge(') sub'); stmt.merge('GROUP BY code'); stmt.merge(conn.makePagination(filter)); diff --git a/back/methods/collection/setSaleQuantity.js b/back/methods/collection/setSaleQuantity.js index 644c44a60..b6c56ddc4 100644 --- a/back/methods/collection/setSaleQuantity.js +++ b/back/methods/collection/setSaleQuantity.js @@ -26,11 +26,30 @@ module.exports = Self => { Self.setSaleQuantity = async(saleId, quantity) => { const models = Self.app.models; + const myOptions = {}; + let tx; - const sale = await models.Sale.findById(saleId); - return await sale.updateAttributes({ - originalQuantity: sale.quantity, - quantity: quantity - }); + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const sale = await models.Sale.findById(saleId, null, myOptions); + const saleUpdated = await sale.updateAttributes({ + originalQuantity: sale.quantity, + quantity: quantity + }, myOptions); + + if (tx) await tx.commit(); + + return saleUpdated; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } }; }; diff --git a/back/methods/collection/spec/setSaleQuantity.spec.js b/back/methods/collection/spec/setSaleQuantity.spec.js index 5d06a4383..63dc3bd2d 100644 --- a/back/methods/collection/spec/setSaleQuantity.spec.js +++ b/back/methods/collection/spec/setSaleQuantity.spec.js @@ -2,15 +2,26 @@ const models = require('vn-loopback/server/server').models; describe('setSaleQuantity()', () => { it('should change quantity sale', async() => { - const saleId = 30; - const newQuantity = 10; + const tx = await models.Ticket.beginTransaction({}); - const originalSale = await models.Sale.findById(saleId); + try { + const options = {transaction: tx}; - await models.Collection.setSaleQuantity(saleId, newQuantity); - const updateSale = await models.Sale.findById(saleId); + const saleId = 30; + const newQuantity = 10; - expect(updateSale.originalQuantity).toEqual(originalSale.quantity); - expect(updateSale.quantity).toEqual(newQuantity); + const originalSale = await models.Sale.findById(saleId, null, options); + + await models.Collection.setSaleQuantity(saleId, newQuantity, options); + const updateSale = await models.Sale.findById(saleId, null, options); + + expect(updateSale.originalQuantity).toEqual(originalSale.quantity); + expect(updateSale.quantity).toEqual(newQuantity); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } }); }); 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/224702/00-notificationProc.sql b/db/changes/224702/00-notificationProc.sql index 475b2e389..2cf11b4f1 100644 --- a/db/changes/224702/00-notificationProc.sql +++ b/db/changes/224702/00-notificationProc.sql @@ -12,14 +12,9 @@ BEGIN * @param vAuthorFk The notification author or %NULL if there is no author * @return The notification id */ - DECLARE vNotificationFk INT; - - SELECT id INTO vNotificationFk - FROM `notification` - WHERE `name` = vNotificationName; INSERT INTO notificationQueue - SET notificationFk = vNotificationFk, + SET notificationFk = vNotificationName, params = vParams, authorFk = vAuthorFk; diff --git a/db/changes/225001/.gitkeep b/db/changes/225001/.gitkeep new file mode 100644 index 000000000..e69de29bb 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-aclTicketLog.sql b/db/changes/225201/00-aclTicketLog.sql new file mode 100644 index 000000000..edba17ab4 --- /dev/null +++ b/db/changes/225201/00-aclTicketLog.sql @@ -0,0 +1,3 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('TicketLog', 'getChanges', 'READ', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/changes/225001/00-entryDeleteRef.sql b/db/changes/225201/00-entryDeleteRef.sql similarity index 100% rename from db/changes/225001/00-entryDeleteRef.sql rename to db/changes/225201/00-entryDeleteRef.sql diff --git a/db/changes/225001/00-invoiceInConfig.sql b/db/changes/225201/00-invoiceInConfig.sql similarity index 100% rename from db/changes/225001/00-invoiceInConfig.sql rename to db/changes/225201/00-invoiceInConfig.sql diff --git a/db/changes/225201/00-mdbApp.sql b/db/changes/225201/00-mdbApp.sql new file mode 100644 index 000000000..3202e3f08 --- /dev/null +++ b/db/changes/225201/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/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-supplier_beforeUpdate.sql b/db/changes/225201/00-supplier_beforeUpdate.sql new file mode 100644 index 000000000..08af8666b --- /dev/null +++ b/db/changes/225201/00-supplier_beforeUpdate.sql @@ -0,0 +1,46 @@ +DROP TRIGGER IF EXISTS `vn`.`supplier_beforeUpdate`; +USE `vn`; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`supplier_beforeUpdate` + BEFORE UPDATE ON `supplier` + FOR EACH ROW +BEGIN + DECLARE vHasChange BOOL; + DECLARE vPayMethodChanged BOOL; + DECLARE vPayMethodHasVerified BOOL; + DECLARE vParams JSON; + DECLARE vOldPayMethodName VARCHAR(20); + DECLARE vNewPayMethodName VARCHAR(20); + + SELECT hasVerified INTO vPayMethodHasVerified + FROM payMethod + WHERE id = NEW.payMethodFk; + + SET vPayMethodChanged = NOT(NEW.payMethodFk <=> OLD.payMethodFk); + + IF vPayMethodChanged THEN + SELECT name INTO vOldPayMethodName + FROM payMethod + WHERE id = OLD.payMethodFk; + SELECT name INTO vNewPayMethodName + FROM payMethod + WHERE id = NEW.payMethodFk; + + SET vParams = JSON_OBJECT( + 'name', NEW.name, + 'oldPayMethod', vOldPayMethodName, + 'newPayMethod', vNewPayMethodName + ); + SELECT util.notification_send('supplier-pay-method-update', vParams, NULL) INTO @id; + END IF; + + SET vHasChange = NOT(NEW.payDemFk <=> OLD.payDemFk AND NEW.payDay <=> OLD.payDay) OR vPayMethodChanged; + + IF vHasChange AND vPayMethodHasVerified THEN + SET NEW.isPayMethodChecked = FALSE; + END IF; + +END$$ +DELIMITER ; diff --git a/db/changes/225201/00-ticketSms.sql b/db/changes/225201/00-ticketSms.sql new file mode 100644 index 000000000..f454f99b1 --- /dev/null +++ b/db/changes/225201/00-ticketSms.sql @@ -0,0 +1,8 @@ +CREATE TABLE `vn`.`ticketSms` ( + `smsFk` mediumint(8) unsigned NOT NULL, + `ticketFk` int(11) DEFAULT NULL, + PRIMARY KEY (`smsFk`), + KEY `ticketSms_FK_1` (`ticketFk`), + CONSTRAINT `ticketSms_FK` FOREIGN KEY (`smsFk`) REFERENCES `sms` (`id`) ON UPDATE CASCADE, + CONSTRAINT `ticketSms_FK_1` FOREIGN KEY (`ticketFk`) REFERENCES `ticket` (`id`) ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci diff --git a/db/changes/225201/00-ticket_canAdvance.sql b/db/changes/225201/00-ticket_canAdvance.sql new file mode 100644 index 000000000..acc4dcc4a --- /dev/null +++ b/db/changes/225201/00-ticket_canAdvance.sql @@ -0,0 +1,104 @@ +DROP PROCEDURE IF EXISTS `vn`.`ticket_canAdvance`; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT) +BEGIN +/** + * Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar. + * + * @param vDateFuture Fecha de los tickets que se quieren adelantar. + * @param vDateToAdvance Fecha a cuando se quiere adelantar. + * @param vWarehouseFk Almacén + */ + + DECLARE vDateInventory DATE; + + SELECT inventoried INTO vDateInventory FROM vn.config; + + DROP TEMPORARY TABLE IF EXISTS tmp.stock; + CREATE TEMPORARY TABLE tmp.stock + (itemFk INT PRIMARY KEY, + amount INT) + ENGINE = MEMORY; + + INSERT INTO tmp.stock(itemFk, amount) + SELECT itemFk, SUM(quantity) amount FROM + ( + SELECT itemFk, quantity + FROM vn.itemTicketOut + WHERE shipped >= vDateInventory + AND shipped < vDateFuture + AND warehouseFk = vWarehouseFk + UNION ALL + SELECT itemFk, quantity + FROM vn.itemEntryIn + WHERE landed >= vDateInventory + AND landed < vDateFuture + AND isVirtualStock = FALSE + AND warehouseInFk = vWarehouseFk + UNION ALL + SELECT itemFk, quantity + FROM vn.itemEntryOut + WHERE shipped >= vDateInventory + AND shipped < vDateFuture + AND warehouseOutFk = vWarehouseFk + ) t + GROUP BY itemFk HAVING amount != 0; + + DROP TEMPORARY TABLE IF EXISTS tmp.filter; + CREATE TEMPORARY TABLE tmp.filter + (INDEX (id)) + SELECT s.ticketFk futureId, + t2.ticketFk id, + sum((s.quantity <= IFNULL(st.amount,0))) hasStock, + count(DISTINCT s.id) saleCount, + t2.state, + t2.stateCode, + st.name futureState, + st.code futureStateCode, + GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt, + t2.ipt, + t.workerFk, + CAST(sum(litros) AS DECIMAL(10,0)) liters, + CAST(count(*) AS DECIMAL(10,0)) `lines`, + t2.shipped, + t.shipped futureShipped, + t2.totalWithVat, + t.totalWithVat futureTotalWithVat + FROM vn.ticket t + JOIN vn.ticketState ts ON ts.ticketFk = t.id + JOIN vn.state st ON st.id = ts.stateFk + JOIN vn.saleVolume sv ON t.id = sv.ticketFk + JOIN (SELECT + t2.id ticketFk, + t2.addressFk, + st.name state, + st.code stateCode, + GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt, + t2.shipped, + t2.totalWithVat + FROM vn.ticket t2 + JOIN vn.sale s ON s.ticketFk = t2.id + JOIN vn.item i ON i.id = s.itemFk + JOIN vn.ticketState ts ON ts.ticketFk = t2.id + JOIN vn.state st ON st.id = ts.stateFk + LEFT JOIN vn.itemPackingType ipt ON ipt.code = i.itemPackingTypeFk + WHERE t2.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance) + AND t2.warehouseFk = vWarehouseFk + GROUP BY t2.id) t2 ON t2.addressFk = t.addressFk + JOIN vn.sale s ON s.ticketFk = t.id + JOIN vn.item i ON i.id = s.itemFk + LEFT JOIN vn.itemPackingType ipt ON ipt.code = i.itemPackingTypeFk + LEFT JOIN tmp.stock st ON st.itemFk = s.itemFk + WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture) + AND t.warehouseFk = vWarehouseFk + GROUP BY t.id; + + DROP TEMPORARY TABLE tmp.stock; +END$$ +DELIMITER ; + +INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) +VALUES + ('Ticket', 'getTicketsAdvance', 'READ', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/changes/225201/00-ticket_canbePostponed.sql b/db/changes/225201/00-ticket_canbePostponed.sql new file mode 100644 index 000000000..572824b4b --- /dev/null +++ b/db/changes/225201/00-ticket_canbePostponed.sql @@ -0,0 +1,73 @@ +DROP PROCEDURE IF EXISTS `vn`.`ticket_canbePostponed`; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canbePostponed`(vOriginDated DATE, vFutureDated DATE, vWarehouseFk INT) +BEGIN +/** + * Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro + * + * @param vOriginDated Fecha en cuestión + * @param vFutureDated Fecha en el futuro a sondear + * @param vWarehouseFk Identificador de vn.warehouse + */ + DROP TEMPORARY TABLE IF EXISTS tmp.filter; + CREATE TEMPORARY TABLE tmp.filter + (INDEX (id)) + SELECT sv.ticketFk id, + sub2.id futureId, + GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt, + CAST(sum(litros) AS DECIMAL(10,0)) liters, + CAST(count(*) AS DECIMAL(10,0)) `lines`, + st.name state, + sub2.iptd futureIpt, + sub2.state futureState, + t.clientFk, + t.warehouseFk, + ts.alertLevel, + t.shipped, + sub2.shipped futureShipped, + t.workerFk, + st.code stateCode, + sub2.code futureStateCode + FROM vn.saleVolume sv + JOIN vn.sale s ON s.id = sv.saleFk + JOIN vn.item i ON i.id = s.itemFk + JOIN vn.ticket t ON t.id = sv.ticketFk + JOIN vn.address a ON a.id = t.addressFk + JOIN vn.province p ON p.id = a.provinceFk + JOIN vn.country c ON c.id = p.countryFk + JOIN vn.ticketState ts ON ts.ticketFk = t.id + JOIN vn.state st ON st.id = ts.stateFk + JOIN vn.alertLevel al ON al.id = ts.alertLevel + LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id + LEFT JOIN ( + SELECT * + FROM ( + SELECT + t.addressFk, + t.id, + t.shipped, + st.name state, + st.code code, + GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd + FROM vn.ticket t + JOIN vn.ticketState ts ON ts.ticketFk = t.id + JOIN vn.state st ON st.id = ts.stateFk + JOIN vn.sale s ON s.ticketFk = t.id + JOIN vn.item i ON i.id = s.itemFk + WHERE t.shipped BETWEEN vFutureDated + AND util.dayend(vFutureDated) + AND t.warehouseFk = vWarehouseFk + GROUP BY t.id + ) sub + GROUP BY sub.addressFk + ) sub2 ON sub2.addressFk = t.addressFk AND t.id != sub2.id + WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated) + AND t.warehouseFk = vWarehouseFk + AND al.code = 'FREE' + AND tp.ticketFk IS NULL + GROUP BY sv.ticketFk + HAVING futureId; +END$$ +DELIMITER ; diff --git a/db/changes/225201/00-ticket_split_merge.sql b/db/changes/225201/00-ticket_split_merge.sql new file mode 100644 index 000000000..a1a6579e6 --- /dev/null +++ b/db/changes/225201/00-ticket_split_merge.sql @@ -0,0 +1,2 @@ +DROP PROCEDURE IF EXISTS `ticket_split`; +DROP PROCEDURE IF EXISTS `ticket_merge`; diff --git a/db/changes/225201/00-utilNotification.sql b/db/changes/225201/00-utilNotification.sql new file mode 100644 index 000000000..74f0de5e2 --- /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 IGNORE INTO `util`.`notificationSubscription` (notificationFk, userFk) VALUES(3, 19663); + diff --git a/db/changes/225201/01-create_stateI18n.sql b/db/changes/225201/01-create_stateI18n.sql new file mode 100644 index 000000000..799360b1e --- /dev/null +++ b/db/changes/225201/01-create_stateI18n.sql @@ -0,0 +1,7 @@ +CREATE TABLE `vn`.`stateI18n` ( + `stateFk` tinyint(3) unsigned NOT NULL, + `lang` char(2) NOT NULL, + `name` varchar(255) NOT NULL, + PRIMARY KEY (`stateFk`, `lang`), + CONSTRAINT `stateI18n_state_id` FOREIGN KEY (`stateFk`) REFERENCES `vn`.`state` (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8; diff --git a/db/changes/225001/01-modules.sql b/db/changes/225201/01-modules.sql similarity index 100% rename from db/changes/225001/01-modules.sql rename to db/changes/225201/01-modules.sql diff --git a/db/changes/225201/02-insert_stateI18n.sql b/db/changes/225201/02-insert_stateI18n.sql new file mode 100644 index 000000000..7df36d662 --- /dev/null +++ b/db/changes/225201/02-insert_stateI18n.sql @@ -0,0 +1,73 @@ +INSERT INTO + `vn`.`stateI18n` (`stateFk`, `lang`, `name`) +VALUES + (1, 'en', 'Fix'), + (1, 'es', 'Arreglar'), + (2, 'en', 'Free'), + (2, 'es', 'Libre'), + (3, 'en', 'OK'), + (3, 'es', 'OK'), + (4, 'en', 'Printed'), + (4, 'es', 'Impreso'), + (5, 'en', 'Preparation'), + (5, 'es', 'Preparación'), + (6, 'en', 'In Review'), + (6, 'es', 'En Revisión'), + (7, 'en', 'Unfinished'), + (7, 'es', 'Sin Acabar'), + (8, 'en', 'Reviewed'), + (8, 'es', 'Revisado'), + (9, 'en', 'Fitting'), + (9, 'es', 'Encajando'), + (10, 'en', 'Fitted'), + (10, 'es', 'Encajado'), + (11, 'en', 'Billed'), + (11, 'es', 'Facturado'), + (12, 'en', 'Blocked'), + (12, 'es', 'Bloqueado'), + (13, 'en', 'In Delivery'), + (13, 'es', 'En Reparto'), + (14, 'en', 'Prepared'), + (14, 'es', 'Preparado'), + (15, 'en', 'Pending Collection'), + (15, 'es', 'Pendiente de Recogida'), + (16, 'en', 'Delivered'), + (16, 'es', 'Entregado'), + (20, 'en', 'Assigned'), + (20, 'es', 'Asignado'), + (21, 'en', 'Returned'), + (21, 'es', 'Retornado'), + (22, 'en', 'Pending to extend'), + (22, 'es', 'Pendiente ampliar'), + (23, 'en', 'URGENT'), + (23, 'es', 'URGENTE'), + (24, 'en', 'Chained'), + (24, 'es', 'Encadenado'), + (25, 'en', 'Shipping'), + (25, 'es', 'Embarcando'), + (26, 'en', 'Preparation'), + (26, 'es', 'Preparación previa'), + (27, 'en', 'Assisted preparation'), + (27, 'es', 'Preparación asistida'), + (28, 'en', 'Preparation OK'), + (28, 'es', 'Previa OK'), + (29, 'en', 'Preparation Printed'), + (29, 'es', 'Previa Impreso'), + (30, 'en', 'Shipped'), + (30, 'es', 'Embarcado'), + (31, 'en', 'Stowaway printed'), + (31, 'es', 'Polizón Impreso'), + (32, 'en', 'Stowaway OK'), + (32, 'es', 'Polizón OK'), + (33, 'en', 'Auto_Printed'), + (33, 'es', 'Auto_Impreso'), + (34, 'en', 'Pending payment'), + (34, 'es', 'Pendiente de pago'), + (35, 'en', 'Half-Embedded'), + (35, 'es', 'Semi-Encajado'), + (36, 'en', 'Preparation Reviewing'), + (36, 'es', 'Previa Revisando'), + (37, 'en', 'Preparation Reviewed'), + (37, 'es', 'Previa Revisado'), + (38, 'en', 'Preparation Chamber'), + (38, 'es', 'Preparación Cámara'); \ No newline at end of file diff --git a/db/changes/225001/02-starredModule.sql b/db/changes/225201/02-starredModule.sql similarity index 100% rename from db/changes/225001/02-starredModule.sql rename to db/changes/225201/02-starredModule.sql diff --git a/db/changes/230201/00-kkearSaleChecked.sql b/db/changes/230201/00-kkearSaleChecked.sql new file mode 100644 index 000000000..03e854bbd --- /dev/null +++ b/db/changes/230201/00-kkearSaleChecked.sql @@ -0,0 +1,2 @@ +DELETE FROM `salix`.`ACL` WHERE model="SaleChecked"; +DROP TABLE IF EXISTS `vn`.`saleChecked`; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 28cd48f13..1ea4fa114 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -690,7 +690,8 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF (27 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Wolverine', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (28, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (29, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), - (30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()); + (30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), + (31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()); INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) VALUES @@ -991,7 +992,8 @@ INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `pric (33, 5, 14, 'Ranged weapon pistol 9mm', 50, 1.79, 0, 0, 0, util.VN_CURDATE()), (34, 4, 28, 'Melee weapon heavy shield 1x0.5m', 20, 1.72, 0, 0, 0, util.VN_CURDATE()), (35, 4, 29, 'Melee weapon heavy shield 1x0.5m', 20, 1.72, 0, 0, 0, util.VN_CURDATE()), - (36, 4, 30, 'Melee weapon heavy shield 1x0.5m', 20, 1.72, 0, 0, 0, util.VN_CURDATE()); + (36, 4, 30, 'Melee weapon heavy shield 1x0.5m', 20, 1.72, 0, 0, 0, util.VN_CURDATE()), + (37, 4, 31, 'Melee weapon heavy shield 1x0.5m', 20, 1.72, 0, 0, 0, util.VN_CURDATE()); INSERT INTO `vn`.`saleChecked`(`saleFk`, `isChecked`) VALUES @@ -1333,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 @@ -2650,7 +2652,7 @@ INSERT INTO `vn`.`mdbVersion` (`app`, `branchFk`, `version`) INSERT INTO `vn`.`accountingConfig` (`id`, `minDate`, `maxDate`) VALUES - (1, '2022-01-01', '2023-01-01'); + (1, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)), '-01-01'), CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR)), '-01-01')); INSERT INTO `vn`.`saleGroup` (`userFk`, `parkingFk`, `sectorFk`) @@ -2689,7 +2691,8 @@ INSERT INTO `util`.`notificationConfig` INSERT INTO `util`.`notification` (`id`, `name`, `description`) VALUES - (1, 'print-email', 'notification fixture one'); + (1, 'print-email', 'notification fixture one'), + (4, 'supplier-pay-method-update', 'A supplier pay method has been updated'); INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) VALUES @@ -2704,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 @@ -2730,14 +2734,27 @@ UPDATE `account`.`user` SET `hasGrant` = 1 WHERE `id` = 66; +INSERT INTO `vn`.`ticketLog` (`originFk`, userFk, `action`, changedModel, oldInstance, newInstance, changedModelId, `description`) + VALUES + (7, 18, 'update', 'Sale', '{"quantity":1}', '{"quantity":10}', 1, NULL), + (7, 18, 'update', 'Ticket', '{"quantity":1,"concept":"Chest ammo box"}', '{"quantity":10,"concept":"Chest ammo box"}', 1, NULL), + (7, 18, 'update', 'Sale', '{"price":3}', '{"price":5}', 1, NULL), + (7, 18, 'update', NULL, NULL, NULL, NULL, "Cambio cantidad Melee weapon heavy shield 1x0.5m de '5' a '10'"); + + INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`) 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/#/'), @@ -2746,3 +2763,4 @@ INSERT INTO `salix`.`url` (`appName`, `environment`, `url`) INSERT INTO `vn`.`payDemDetail` (`id`, `detail`) VALUES (1, 1); + 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 f550e3a9d..e1792ea7b 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -30,7 +30,7 @@ export default { firstModuleRemovePinIcon: 'vn-home a:nth-child(1) vn-icon[icon="remove_circle"]' }, recoverPassword: { - recoverPasswordButton: 'vn-login a[ui-sref="recoverPassword"]', + recoverPasswordButton: 'vn-login a[ui-sref="recover-password"]', email: 'vn-recover-password vn-textfield[ng-model="$ctrl.email"]', sendEmailButton: 'vn-recover-password vn-submit', }, @@ -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', @@ -735,18 +738,16 @@ export default { }, ticketFuture: { openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', - originDated: 'vn-date-picker[label="Origin ETD"]', - futureDated: 'vn-date-picker[label="Destination ETD"]', - shipped: 'vn-date-picker[label="Origin date"]', - tfShipped: 'vn-date-picker[label="Destination date"]', + originDated: 'vn-date-picker[label="Origin date"]', + futureDated: 'vn-date-picker[label="Destination date"]', linesMax: 'vn-textfield[label="Max Lines"]', litersMax: 'vn-textfield[label="Max Liters"]', ipt: 'vn-autocomplete[label="Origin IPT"]', - tfIpt: 'vn-autocomplete[label="Destination IPT"]', + futureIpt: 'vn-autocomplete[label="Destination IPT"]', tableIpt: 'vn-autocomplete[name="ipt"]', - tableTfIpt: 'vn-autocomplete[name="tfIpt"]', + tableFutureIpt: 'vn-autocomplete[name="futureIpt"]', state: 'vn-autocomplete[label="Origin Grouped State"]', - tfState: 'vn-autocomplete[label="Destination Grouped State"]', + futureState: 'vn-autocomplete[label="Destination Grouped State"]', warehouseFk: 'vn-autocomplete[label="Warehouse"]', problems: 'vn-check[label="With problems"]', tableButtonSearch: 'vn-button[vn-tooltip="Search"]', @@ -755,9 +756,34 @@ export default { firstCheck: 'tbody > tr:nth-child(1) > td > vn-check', multiCheck: 'vn-multi-check', tableId: 'vn-textfield[name="id"]', - tableTfId: 'vn-textfield[name="ticketFuture"]', - tableLiters: 'vn-textfield[name="litersMax"]', - tableLines: 'vn-textfield[name="linesMax"]', + tableFutureId: 'vn-textfield[name="futureId"]', + tableLiters: 'vn-textfield[name="liters"]', + tableLines: 'vn-textfield[name="lines"]', + submit: 'vn-submit[label="Search"]', + table: 'tbody > tr:not(.empty-rows)' + }, + ticketAdvance: { + openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', + dateFuture: 'vn-date-picker[label="Origin date"]', + dateToAdvance: 'vn-date-picker[label="Destination date"]', + linesMax: 'vn-textfield[label="Max Lines"]', + litersMax: 'vn-textfield[label="Max Liters"]', + futureIpt: 'vn-autocomplete[label="Origin IPT"]', + ipt: 'vn-autocomplete[label="Destination IPT"]', + tableIpt: 'vn-autocomplete[name="ipt"]', + tableFutureIpt: 'vn-autocomplete[name="futureIpt"]', + futureState: 'vn-autocomplete[label="Origin Grouped State"]', + state: 'vn-autocomplete[label="Destination Grouped State"]', + warehouseFk: 'vn-autocomplete[label="Warehouse"]', + tableButtonSearch: 'vn-button[vn-tooltip="Search"]', + moveButton: 'vn-button[vn-tooltip="Advance tickets"]', + acceptButton: '.vn-confirm.shown button[response="accept"]', + multiCheck: 'vn-multi-check', + tableId: 'vn-textfield[name="id"]', + tableFutureId: 'vn-textfield[name="futureId"]', + tableLiters: 'vn-textfield[name="liters"]', + tableLines: 'vn-textfield[name="lines"]', + tableStock: 'vn-textfield[name="hasStock"]', submit: 'vn-submit[label="Search"]', table: 'tbody > tr:not(.empty-rows)' }, @@ -1100,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/01-salix/04_recoverPassword.spec.js b/e2e/paths/01-salix/04_recoverPassword.spec.js index 80ef32cb5..e6cb02ab1 100644 --- a/e2e/paths/01-salix/04_recoverPassword.spec.js +++ b/e2e/paths/01-salix/04_recoverPassword.spec.js @@ -1,7 +1,7 @@ import selectors from '../../helpers/selectors'; import getBrowser from '../../helpers/puppeteer'; -describe('Login path', async() => { +describe('RecoverPassword path', async() => { let browser; let page; @@ -10,7 +10,7 @@ describe('Login path', async() => { page = browser.page; await page.waitToClick(selectors.recoverPassword.recoverPasswordButton); - await page.waitForState('recoverPassword'); + await page.waitForState('recover-password'); }); afterAll(async() => { 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/03-worker/04_time_control.spec.js b/e2e/paths/03-worker/04_time_control.spec.js index be8df3cf0..4236ae0e4 100644 --- a/e2e/paths/03-worker/04_time_control.spec.js +++ b/e2e/paths/03-worker/04_time_control.spec.js @@ -58,6 +58,7 @@ describe('Worker time control path', () => { }); it(`should return error when insert 'out' of first entry`, async() => { + pending('https://redmine.verdnatura.es/issues/4707'); await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm); await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out'); @@ -68,6 +69,7 @@ describe('Worker time control path', () => { }); it(`should insert 'in' monday`, async() => { + pending('https://redmine.verdnatura.es/issues/4707'); await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm); await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in'); @@ -78,6 +80,7 @@ describe('Worker time control path', () => { }); it(`should insert 'out' monday`, async() => { + pending('https://redmine.verdnatura.es/issues/4707'); await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); await page.pickTime(selectors.workerTimeControl.dialogTimeInput, fourPm); await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out'); @@ -88,11 +91,13 @@ describe('Worker time control path', () => { }); it(`should check Hank Pym worked 8:20 hours`, async() => { + pending('https://redmine.verdnatura.es/issues/4707'); await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '08:20 h.'); await page.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '08:20 h.'); }); it('should remove first entry of monday', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); await page.waitForTextInElement(selectors.workerTimeControl.firstEntryOfMonday, eightAm); await page.waitForTextInElement(selectors.workerTimeControl.secondEntryOfMonday, fourPm); await page.waitToClick(selectors.workerTimeControl.firstEntryOfMondayDelete); @@ -103,6 +108,7 @@ describe('Worker time control path', () => { }); it(`should be the 'out' the first entry of monday`, async() => { + pending('https://redmine.verdnatura.es/issues/4707'); const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText'); expect(result).toEqual(fourPm); diff --git a/e2e/paths/04-item/08_regularize.spec.js b/e2e/paths/04-item/08_regularize.spec.js index 2e09a9f63..9b3074776 100644 --- a/e2e/paths/04-item/08_regularize.spec.js +++ b/e2e/paths/04-item/08_regularize.spec.js @@ -127,8 +127,8 @@ describe('Item regularize path', () => { await page.waitForState('ticket.index'); }); - it('should search for the ticket with id 31 once again', async() => { - await page.accessToSearchResult('31'); + it('should search for the ticket missing once again', async() => { + await page.accessToSearchResult('Missing'); await page.waitForState('ticket.card.summary'); }); 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/05-ticket/20_future.spec.js b/e2e/paths/05-ticket/21_future.spec.js similarity index 71% rename from e2e/paths/05-ticket/20_future.spec.js rename to e2e/paths/05-ticket/21_future.spec.js index 6db2bf4f0..45c39de86 100644 --- a/e2e/paths/05-ticket/20_future.spec.js +++ b/e2e/paths/05-ticket/21_future.spec.js @@ -16,9 +16,6 @@ describe('Ticket Future path', () => { await browser.close(); }); - const now = new Date(); - const tomorrow = new Date(now.getDate() + 1); - it('should show errors snackbar because of the required data', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.warehouseFk); @@ -27,20 +24,6 @@ describe('Ticket Future path', () => { expect(message.text).toContain('warehouseFk is a required argument'); - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.litersMax); - await page.waitToClick(selectors.ticketFuture.submit); - message = await page.waitForSnackbar(); - - expect(message.text).toContain('litersMax is a required argument'); - - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.linesMax); - await page.waitToClick(selectors.ticketFuture.submit); - message = await page.waitForSnackbar(); - - expect(message.text).toContain('linesMax is a required argument'); - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.clearInput(selectors.ticketFuture.futureDated); await page.waitToClick(selectors.ticketFuture.submit); @@ -62,44 +45,13 @@ describe('Ticket Future path', () => { await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); }); - it('should search with the origin shipped today', async() => { - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.pickDate(selectors.ticketFuture.shipped, now); - await page.waitToClick(selectors.ticketFuture.submit); - await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); - }); - - it('should search with the origin shipped tomorrow', async() => { - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.pickDate(selectors.ticketFuture.shipped, tomorrow); - await page.waitToClick(selectors.ticketFuture.submit); - await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); - }); - - it('should search with the destination shipped today', async() => { - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.shipped); - await page.pickDate(selectors.ticketFuture.tfShipped, now); - await page.waitToClick(selectors.ticketFuture.submit); - await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); - }); - - it('should search with the destination shipped tomorrow', async() => { - await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.pickDate(selectors.ticketFuture.tfShipped, tomorrow); - await page.waitToClick(selectors.ticketFuture.submit); - await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); - }); - it('should search with the origin IPT', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.shipped); - await page.clearInput(selectors.ticketFuture.tfShipped); await page.clearInput(selectors.ticketFuture.ipt); - await page.clearInput(selectors.ticketFuture.tfIpt); + await page.clearInput(selectors.ticketFuture.futureIpt); await page.clearInput(selectors.ticketFuture.state); - await page.clearInput(selectors.ticketFuture.tfState); + await page.clearInput(selectors.ticketFuture.futureState); await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal'); await page.waitToClick(selectors.ticketFuture.submit); @@ -109,14 +61,12 @@ describe('Ticket Future path', () => { it('should search with the destination IPT', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.shipped); - await page.clearInput(selectors.ticketFuture.tfShipped); await page.clearInput(selectors.ticketFuture.ipt); - await page.clearInput(selectors.ticketFuture.tfIpt); + await page.clearInput(selectors.ticketFuture.futureIpt); await page.clearInput(selectors.ticketFuture.state); - await page.clearInput(selectors.ticketFuture.tfState); + await page.clearInput(selectors.ticketFuture.futureState); - await page.autocompleteSearch(selectors.ticketFuture.tfIpt, 'Horizontal'); + await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'Horizontal'); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); }); @@ -124,12 +74,10 @@ describe('Ticket Future path', () => { it('should search with the origin grouped state', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.shipped); - await page.clearInput(selectors.ticketFuture.tfShipped); await page.clearInput(selectors.ticketFuture.ipt); - await page.clearInput(selectors.ticketFuture.tfIpt); + await page.clearInput(selectors.ticketFuture.futureIpt); await page.clearInput(selectors.ticketFuture.state); - await page.clearInput(selectors.ticketFuture.tfState); + await page.clearInput(selectors.ticketFuture.futureState); await page.autocompleteSearch(selectors.ticketFuture.state, 'Free'); await page.waitToClick(selectors.ticketFuture.submit); @@ -139,24 +87,20 @@ describe('Ticket Future path', () => { it('should search with the destination grouped state', async() => { await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.shipped); - await page.clearInput(selectors.ticketFuture.tfShipped); await page.clearInput(selectors.ticketFuture.ipt); - await page.clearInput(selectors.ticketFuture.tfIpt); + await page.clearInput(selectors.ticketFuture.futureIpt); await page.clearInput(selectors.ticketFuture.state); - await page.clearInput(selectors.ticketFuture.tfState); + await page.clearInput(selectors.ticketFuture.futureState); - await page.autocompleteSearch(selectors.ticketFuture.tfState, 'Free'); + await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free'); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 0); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); - await page.clearInput(selectors.ticketFuture.shipped); - await page.clearInput(selectors.ticketFuture.tfShipped); await page.clearInput(selectors.ticketFuture.ipt); - await page.clearInput(selectors.ticketFuture.tfIpt); + await page.clearInput(selectors.ticketFuture.futureIpt); await page.clearInput(selectors.ticketFuture.state); - await page.clearInput(selectors.ticketFuture.tfState); + await page.clearInput(selectors.ticketFuture.futureState); await page.waitToClick(selectors.ticketFuture.submit); await page.waitForNumberOfElements(selectors.ticketFuture.table, 4); @@ -176,7 +120,7 @@ describe('Ticket Future path', () => { it('should search in smart-table with an ID Destination', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.write(selectors.ticketFuture.tableTfId, '12'); + await page.write(selectors.ticketFuture.tableFutureId, '12'); await page.keyboard.press('Enter'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 5); @@ -199,7 +143,7 @@ describe('Ticket Future path', () => { it('should search in smart-table with an IPT Destination', async() => { await page.waitToClick(selectors.ticketFuture.tableButtonSearch); - await page.autocompleteSearch(selectors.ticketFuture.tableTfIpt, 'Vertical'); + await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Vertical'); await page.waitForNumberOfElements(selectors.ticketFuture.table, 1); await page.waitToClick(selectors.ticketFuture.tableButtonSearch); diff --git a/e2e/paths/05-ticket/22_advance.spec.js b/e2e/paths/05-ticket/22_advance.spec.js new file mode 100644 index 000000000..6aaa81591 --- /dev/null +++ b/e2e/paths/05-ticket/22_advance.spec.js @@ -0,0 +1,162 @@ +import selectors from '../../helpers/selectors.js'; +import getBrowser from '../../helpers/puppeteer'; + +describe('Ticket Advance path', () => { + let browser; + let page; + + beforeAll(async() => { + browser = await getBrowser(); + page = browser.page; + await page.loginAndModule('employee', 'ticket'); + await page.accessToSection('ticket.advance'); + }); + + afterAll(async() => { + await browser.close(); + }); + + it('should show errors snackbar because of the required data', async() => { + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.warehouseFk); + + await page.waitToClick(selectors.ticketAdvance.submit); + let message = await page.waitForSnackbar(); + + expect(message.text).toContain('warehouseFk is a required argument'); + + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.dateToAdvance); + await page.waitToClick(selectors.ticketAdvance.submit); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('dateToAdvance is a required argument'); + + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.dateFuture); + await page.waitToClick(selectors.ticketAdvance.submit); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('dateFuture is a required argument'); + }); + + it('should search with the required data', async() => { + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search with the origin IPT', async() => { + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal'); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0); + + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.ipt); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search with the destination IPT', async() => { + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal'); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0); + + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.futureIpt); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search with the origin grouped state', async() => { + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.autocompleteSearch(selectors.ticketAdvance.futureState, 'Free'); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.futureState); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search with the destination grouped state', async() => { + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.autocompleteSearch(selectors.ticketAdvance.state, 'Free'); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0); + + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.clearInput(selectors.ticketAdvance.state); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search in smart-table with an IPT Origin', async() => { + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'Vertical'); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search in smart-table with an IPT Destination', async() => { + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'Vertical'); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search in smart-table with stock', async() => { + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.write(selectors.ticketAdvance.tableStock, '5'); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 2); + + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search in smart-table with especified Lines', async() => { + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.write(selectors.ticketAdvance.tableLines, '0'); + await page.keyboard.press('Enter'); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should search in smart-table with especified Liters', async() => { + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.write(selectors.ticketAdvance.tableLiters, '0'); + await page.keyboard.press('Enter'); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + + await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); + await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); + await page.waitToClick(selectors.ticketAdvance.submit); + await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1); + }); + + it('should check the three last tickets and move to the future', async() => { + await page.waitToClick(selectors.ticketAdvance.multiCheck); + await page.waitToClick(selectors.ticketAdvance.moveButton); + await page.waitToClick(selectors.ticketAdvance.acceptButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Tickets moved successfully!'); + }); +}); 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/core/components/smart-table/index.js b/front/core/components/smart-table/index.js index 8d2c3c153..770dcdf32 100644 --- a/front/core/components/smart-table/index.js +++ b/front/core/components/smart-table/index.js @@ -147,7 +147,7 @@ export default class SmartTable extends Component { for (const column of this.columns) { if (viewConfig.configuration[column.field] == false) { const baseSelector = `smart-table[view-config-id="${this.viewConfigId}"] table`; - selectors.push(`${baseSelector} thead > tr > th:nth-child(${column.index + 1})`); + selectors.push(`${baseSelector} thead > tr:not([second-header]) > th:nth-child(${column.index + 1})`); selectors.push(`${baseSelector} tbody > tr > td:nth-child(${column.index + 1})`); } } @@ -235,7 +235,7 @@ export default class SmartTable extends Component { } registerColumns() { - const header = this.element.querySelector('thead > tr'); + const header = this.element.querySelector('thead > tr:not([second-header])'); if (!header) return; const columns = header.querySelectorAll('th'); @@ -254,7 +254,7 @@ export default class SmartTable extends Component { } emptyDataRows() { - const header = this.element.querySelector('thead > tr'); + const header = this.element.querySelector('thead > tr:not([second-header])'); const columns = header.querySelectorAll('th'); const tbody = this.element.querySelector('tbody'); if (tbody) { @@ -333,7 +333,7 @@ export default class SmartTable extends Component { } displaySearch() { - const header = this.element.querySelector('thead > tr'); + const header = this.element.querySelector('thead > tr:not([second-header])'); if (!header) return; const tbody = this.element.querySelector('tbody'); diff --git a/front/core/components/smart-table/table.scss b/front/core/components/smart-table/table.scss index c38c149ca..996c41a74 100644 --- a/front/core/components/smart-table/table.scss +++ b/front/core/components/smart-table/table.scss @@ -8,6 +8,16 @@ smart-table table { & > thead { border-bottom: $border; + & > tr[second-header] { + & > th + { + text-align: center; + border-bottom-style: groove; + font-weight: bold; + text-transform: uppercase; + } + } + & > * > th { font-weight: normal; } @@ -60,6 +70,9 @@ smart-table table { vertical-align: middle; } } + &[separator]{ + border-left-style: groove; + } vn-icon.bright, i.bright { color: #f7931e; } @@ -108,4 +121,4 @@ smart-table table { font-size: 1.375rem; text-align: center; } -} \ No newline at end of file +} diff --git a/front/core/services/auth.js b/front/core/services/auth.js index 04520cd0b..c15a34d94 100644 --- a/front/core/services/auth.js +++ b/front/core/services/auth.js @@ -24,7 +24,7 @@ export default class Auth { initialize() { let criteria = { to: state => { - const outLayout = ['login', 'recoverPassword', 'resetPassword']; + const outLayout = ['login', 'recover-password', 'reset-password']; return !outLayout.some(ol => ol == state.name); } }; 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/app/app.html b/front/salix/components/app/app.html index f14fab2dd..d6169308a 100644 --- a/front/salix/components/app/app.html +++ b/front/salix/components/app/app.html @@ -1,8 +1,3 @@ - - - - + diff --git a/front/salix/components/app/app.js b/front/salix/components/app/app.js index 20f0ad969..97384d1e1 100644 --- a/front/salix/components/app/app.js +++ b/front/salix/components/app/app.js @@ -19,12 +19,6 @@ export default class App extends Component { this.vnApp.logger = this; } - get showLayout() { - const state = this.$state.current.name || this.$location.$$path.substring(1).replace('/', '.'); - const outLayout = ['login', 'recoverPassword', 'resetPassword']; - return state && !outLayout.some(ol => ol == state); - } - $onDestroy() { this.deregisterCallback(); this.vnApp.logger = null; diff --git a/front/salix/components/index.js b/front/salix/components/index.js index dbe9fe81a..f6727fadf 100644 --- a/front/salix/components/index.js +++ b/front/salix/components/index.js @@ -5,10 +5,10 @@ import './descriptor-popover'; import './home/home'; import './layout'; import './left-menu/left-menu'; -import './login/index'; -import './login/login'; -import './login/recover-password'; -import './login/reset-password'; +import './login'; +import './outLayout'; +import './recover-password'; +import './reset-password'; import './module-card'; import './module-main'; import './side-menu/side-menu'; @@ -19,3 +19,4 @@ import './user-popover'; import './upload-photo'; import './bank-entity'; import './log'; +import './sendSms'; 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}} -
-
+ + + + + + + + + + + + + + + + +
FieldBeforeAfter
{{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: ' 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/front/salix/components/login/index.html b/front/salix/components/login/index.html index 186979f8c..963c23061 100644 --- a/front/salix/components/login/index.html +++ b/front/salix/components/login/index.html @@ -1,6 +1,27 @@ -
- -
- -
+ + + + + + + diff --git a/front/salix/components/login/index.js b/front/salix/components/login/index.js index f0e21fa29..150e896a1 100644 --- a/front/salix/components/login/index.js +++ b/front/salix/components/login/index.js @@ -1,16 +1,43 @@ import ngModule from '../../module'; -import Component from 'core/lib/component'; import './style.scss'; -export default class OutLayout extends Component { - constructor($element, $scope) { - super($element, $scope); +/** + * A simple login form. + */ +export default class Controller { + constructor($, $element, vnAuth) { + Object.assign(this, { + $, + $element, + vnAuth, + user: localStorage.getItem('lastUser'), + remember: true + }); + } + + submit() { + this.loading = true; + this.vnAuth.login(this.user, this.password, this.remember) + .then(() => { + localStorage.setItem('lastUser', this.user); + this.loading = false; + }) + .catch(err => { + this.loading = false; + this.password = ''; + this.focusUser(); + throw err; + }); + } + + focusUser() { + this.$.userField.select(); + this.$.userField.focus(); } } +Controller.$inject = ['$scope', '$element', 'vnAuth']; -OutLayout.$inject = ['$element', '$scope']; - -ngModule.vnComponent('vnOutLayout', { +ngModule.vnComponent('vnLogin', { template: require('./index.html'), - controller: OutLayout + controller: Controller }); diff --git a/front/salix/components/login/locale/es.yml b/front/salix/components/login/locale/es.yml index e3a5815c1..c34861bfb 100644 --- a/front/salix/components/login/locale/es.yml +++ b/front/salix/components/login/locale/es.yml @@ -1,16 +1,5 @@ User: Usuario Password: Contraseña -Email: Correo electrónico Do not close session: No cerrar sesión Enter: Entrar I do not remember my password: No recuerdo mi contraseña -Recover password: Recuperar contraseña -We will sent you an email to recover your password: Te enviaremos un correo para restablecer tu contraseña -Notification sent!: ¡Notificación enviada! -Reset password: Restrablecer contraseña -New password: Nueva contraseña -Repeat password: Repetir contraseña -Password requirements: > - La contraseña debe tener al menos {{ length }} caracteres de longitud, - {{nAlpha}} caracteres alfabéticos, {{nUpper}} letras mayúsculas, {{nDigits}} - dígitos y {{nPunct}} símbolos (Ej: $%&.) diff --git a/front/salix/components/login/login.html b/front/salix/components/login/login.html deleted file mode 100644 index a078fa0af..000000000 --- a/front/salix/components/login/login.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/front/salix/components/login/login.js b/front/salix/components/login/login.js deleted file mode 100644 index b5f8c1e7d..000000000 --- a/front/salix/components/login/login.js +++ /dev/null @@ -1,43 +0,0 @@ -import ngModule from '../../module'; -import './style.scss'; - -/** - * A simple login form. - */ -export default class Controller { - constructor($, $element, vnAuth) { - Object.assign(this, { - $, - $element, - vnAuth, - user: localStorage.getItem('lastUser'), - remember: true - }); - } - - submit() { - this.loading = true; - this.vnAuth.login(this.user, this.password, this.remember) - .then(() => { - localStorage.setItem('lastUser', this.user); - this.loading = false; - }) - .catch(err => { - this.loading = false; - this.password = ''; - this.focusUser(); - throw err; - }); - } - - focusUser() { - this.$.userField.select(); - this.$.userField.focus(); - } -} -Controller.$inject = ['$scope', '$element', 'vnAuth']; - -ngModule.vnComponent('vnLogin', { - template: require('./login.html'), - controller: Controller -}); diff --git a/front/salix/components/login/style.scss b/front/salix/components/login/style.scss index 8985893f2..f13c9cf86 100644 --- a/front/salix/components/login/style.scss +++ b/front/salix/components/login/style.scss @@ -1,8 +1,6 @@ @import "variables"; -vn-login, -vn-reset-password, -vn-recover-password{ +vn-login{ .footer { margin-top: 32px; text-align: center; @@ -24,69 +22,3 @@ vn-recover-password{ } } } - -vn-out-layout{ - position: absolute; - height: 100%; - width: 100%; - margin: 0; - padding: 0; - color: $color-font; - font-size: 1.1rem; - font-weight: normal; - background-color: $color-bg-dark; - display: flex; - justify-content: center; - align-items: center; - overflow: auto; - - & > .box { - box-sizing: border-box; - position: absolute; - max-width: 304px; - min-width: 240px; - padding: 48px; - background-color: $color-bg-panel; - box-shadow: 0 0 16px 0 rgba(0, 0, 0, .6); - border-radius: 8px; - - & > img { - width: 100%; - padding-bottom: 16px; - } - & > form { - & > .vn-textfield { - width: 100%; - } - & > .vn-check { - display: block; - .md-label { - white-space: inherit; - } - } - } - - h5{ - color: $color-primary; - } - - .text-secondary{ - text-align: center; - padding-bottom: 16px; - } - - } - - @media screen and (max-width: 600px) { - background-color: $color-bg-panel; - - & > .box { - padding: 16px; - box-shadow: none; - } - } - - a{ - color: $color-primary; - } -} diff --git a/front/salix/components/outLayout/index.html b/front/salix/components/outLayout/index.html new file mode 100644 index 000000000..186979f8c --- /dev/null +++ b/front/salix/components/outLayout/index.html @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/front/salix/components/outLayout/index.js b/front/salix/components/outLayout/index.js new file mode 100644 index 000000000..f0e21fa29 --- /dev/null +++ b/front/salix/components/outLayout/index.js @@ -0,0 +1,16 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export default class OutLayout extends Component { + constructor($element, $scope) { + super($element, $scope); + } +} + +OutLayout.$inject = ['$element', '$scope']; + +ngModule.vnComponent('vnOutLayout', { + template: require('./index.html'), + controller: OutLayout +}); diff --git a/front/salix/components/login/logo.svg b/front/salix/components/outLayout/logo.svg similarity index 100% rename from front/salix/components/login/logo.svg rename to front/salix/components/outLayout/logo.svg diff --git a/front/salix/components/outLayout/style.scss b/front/salix/components/outLayout/style.scss new file mode 100644 index 000000000..aa94fefb3 --- /dev/null +++ b/front/salix/components/outLayout/style.scss @@ -0,0 +1,67 @@ +@import "variables"; + +vn-out-layout{ + position: absolute; + height: 100%; + width: 100%; + margin: 0; + padding: 0; + color: $color-font; + font-size: 1.1rem; + font-weight: normal; + background-color: $color-bg-dark; + display: flex; + justify-content: center; + align-items: center; + overflow: auto; + + & > .box { + box-sizing: border-box; + position: absolute; + max-width: 304px; + min-width: 240px; + padding: 48px; + background-color: $color-bg-panel; + box-shadow: 0 0 16px 0 rgba(0, 0, 0, .6); + border-radius: 8px; + + & > img { + width: 100%; + padding-bottom: 16px; + } + & > form { + & > .vn-textfield { + width: 100%; + } + & > .vn-check { + display: block; + .md-label { + white-space: inherit; + } + } + } + + h5{ + color: $color-primary; + } + + .text-secondary{ + text-align: center; + padding-bottom: 16px; + } + + } + + @media screen and (max-width: 600px) { + background-color: $color-bg-panel; + + & > .box { + padding: 16px; + box-shadow: none; + } + } + + a{ + color: $color-primary; + } +} diff --git a/front/salix/components/login/recover-password.html b/front/salix/components/recover-password/index.html similarity index 100% rename from front/salix/components/login/recover-password.html rename to front/salix/components/recover-password/index.html diff --git a/front/salix/components/login/recover-password.js b/front/salix/components/recover-password/index.js similarity index 94% rename from front/salix/components/login/recover-password.js rename to front/salix/components/recover-password/index.js index fa9bfc459..3de7f3266 100644 --- a/front/salix/components/login/recover-password.js +++ b/front/salix/components/recover-password/index.js @@ -32,6 +32,6 @@ export default class Controller { Controller.$inject = ['$scope', '$element', '$http', 'vnApp', '$translate', '$state']; ngModule.vnComponent('vnRecoverPassword', { - template: require('./recover-password.html'), + template: require('./index.html'), controller: Controller }); diff --git a/front/salix/components/recover-password/locale/es.yml b/front/salix/components/recover-password/locale/es.yml new file mode 100644 index 000000000..b71c71415 --- /dev/null +++ b/front/salix/components/recover-password/locale/es.yml @@ -0,0 +1,3 @@ +Recover password: Recuperar contraseña +We will sent you an email to recover your password: Te enviaremos un correo para restablecer tu contraseña +Notification sent!: ¡Notificación enviada! diff --git a/front/salix/components/recover-password/style.scss b/front/salix/components/recover-password/style.scss new file mode 100644 index 000000000..d3c6f594e --- /dev/null +++ b/front/salix/components/recover-password/style.scss @@ -0,0 +1,24 @@ +@import "variables"; + +vn-recover-password{ + .footer { + margin-top: 32px; + text-align: center; + position: relative; + & > .vn-submit { + display: block; + + & > input { + display: block; + width: 100%; + } + } + & > .spinner-wrapper { + position: absolute; + width: 0; + top: 3px; + right: -8px; + overflow: visible; + } + } +} diff --git a/front/salix/components/login/reset-password.html b/front/salix/components/reset-password/index.html similarity index 100% rename from front/salix/components/login/reset-password.html rename to front/salix/components/reset-password/index.html diff --git a/front/salix/components/login/reset-password.js b/front/salix/components/reset-password/index.js similarity index 96% rename from front/salix/components/login/reset-password.js rename to front/salix/components/reset-password/index.js index 9ee1fdb62..20c6c34fe 100644 --- a/front/salix/components/login/reset-password.js +++ b/front/salix/components/reset-password/index.js @@ -43,6 +43,6 @@ export default class Controller { Controller.$inject = ['$scope', '$element', '$http', 'vnApp', '$translate', '$state', '$location']; ngModule.vnComponent('vnResetPassword', { - template: require('./reset-password.html'), + template: require('./index.html'), controller: Controller }); diff --git a/front/salix/components/login/locale/en.yml b/front/salix/components/reset-password/locale/en.yml similarity index 71% rename from front/salix/components/login/locale/en.yml rename to front/salix/components/reset-password/locale/en.yml index 1ddd454b7..e5419e1c8 100644 --- a/front/salix/components/login/locale/en.yml +++ b/front/salix/components/reset-password/locale/en.yml @@ -1,7 +1,3 @@ -User: User -Password: Password -Do not close session: Do not close session -Enter: Enter Password requirements: > The password must have at least {{ length }} length characters, {{nAlpha}} alphabetic characters, {{nUpper}} capital letters, {{nDigits}} diff --git a/front/salix/components/reset-password/locale/es.yml b/front/salix/components/reset-password/locale/es.yml new file mode 100644 index 000000000..0771d5dc3 --- /dev/null +++ b/front/salix/components/reset-password/locale/es.yml @@ -0,0 +1,8 @@ +Reset password: Restrablecer contraseña +New password: Nueva contraseña +Repeat password: Repetir contraseñaç +Password changed!: ¡Contraseña cambiada! +Password requirements: > + La contraseña debe tener al menos {{ length }} caracteres de longitud, + {{nAlpha}} caracteres alfabéticos, {{nUpper}} letras mayúsculas, {{nDigits}} + dígitos y {{nPunct}} símbolos (Ej: $%&.) diff --git a/front/salix/components/reset-password/style.scss b/front/salix/components/reset-password/style.scss new file mode 100644 index 000000000..87e4adc8c --- /dev/null +++ b/front/salix/components/reset-password/style.scss @@ -0,0 +1,24 @@ +@import "variables"; + +vn-reset-password{ + .footer { + margin-top: 32px; + text-align: center; + position: relative; + & > .vn-submit { + display: block; + + & > input { + display: block; + width: 100%; + } + } + & > .spinner-wrapper { + position: absolute; + width: 0; + top: 3px; + right: -8px; + overflow: visible; + } + } +} diff --git a/modules/client/front/sms/index.html b/front/salix/components/sendSms/index.html similarity index 100% rename from modules/client/front/sms/index.html rename to front/salix/components/sendSms/index.html diff --git a/modules/ticket/front/sms/index.js b/front/salix/components/sendSms/index.js similarity index 52% rename from modules/ticket/front/sms/index.js rename to front/salix/components/sendSms/index.js index 6bc252dc1..0947550b0 100644 --- a/modules/ticket/front/sms/index.js +++ b/front/salix/components/sendSms/index.js @@ -1,19 +1,26 @@ -import ngModule from '../module'; -import Component from 'core/lib/component'; +import ngModule from '../../module'; import './style.scss'; +import Dialog from '../../../core/components/dialog'; + +export default class sendSmsDialog extends Dialog { + constructor($element, $scope, $http, $translate, vnApp) { + super($element, $scope, $http, $translate, vnApp); + + new CustomEvent('openSmsDialog', { + detail: { + this: this + } + }); + } -class Controller extends Component { open() { this.$.SMSDialog.show(); } charactersRemaining() { - const element = this.$.message; - const value = element.input.value; - + const element = this.sms.message; const maxLength = 160; - const textAreaLength = new Blob([value]).size; - return maxLength - textAreaLength; + return maxLength - element.length; } onResponse() { @@ -25,23 +32,19 @@ class Controller extends Component { if (this.charactersRemaining() < 0) throw new Error(`The message it's too long`); - this.$http.post(`Tickets/${this.sms.ticketId}/sendSms`, this.sms).then(res => { - this.vnApp.showMessage(this.$t('SMS sent!')); - - if (res.data) this.emit('send', {response: res.data}); - }); + return this.onSend({$sms: this.sms}); } catch (e) { this.vnApp.showError(this.$t(e.message)); return false; } - return true; } } -ngModule.vnComponent('vnTicketSms', { +ngModule.vnComponent('vnSmsDialog', { template: require('./index.html'), - controller: Controller, + controller: sendSmsDialog, bindings: { sms: '<', + onSend: '&', } }); diff --git a/modules/client/front/sms/locale/es.yml b/front/salix/components/sendSms/locale/es.yml similarity index 100% rename from modules/client/front/sms/locale/es.yml rename to front/salix/components/sendSms/locale/es.yml diff --git a/modules/client/front/sms/style.scss b/front/salix/components/sendSms/style.scss similarity index 100% rename from modules/client/front/sms/style.scss rename to front/salix/components/sendSms/style.scss diff --git a/front/salix/routes.js b/front/salix/routes.js index be893800f..f32c143ef 100644 --- a/front/salix/routes.js +++ b/front/salix/routes.js @@ -3,25 +3,38 @@ import getMainRoute from 'core/lib/get-main-route'; config.$inject = ['$stateProvider', '$urlRouterProvider']; function config($stateProvider, $urlRouterProvider) { - $urlRouterProvider.otherwise('/'); + $urlRouterProvider + .otherwise('/'); $stateProvider + .state('layout', { + abstract: true, + template: '', + }) + .state('outLayout', { + abstract: true, + template: '', + }) .state('login', { + parent: 'outLayout', url: '/login?continue', description: 'Login', template: '' }) - .state('recoverPassword', { + .state('recover-password', { + parent: 'outLayout', url: '/recover-password', - description: 'Recover-password', - template: 'asd' + description: 'Recover password', + template: '' }) - .state('resetPassword', { + .state('reset-password', { + parent: 'outLayout', url: '/reset-password', - description: 'Reset-password', + description: 'Reset password', template: '' }) .state('home', { + parent: 'layout', url: '/', description: 'Home', template: '' @@ -52,6 +65,10 @@ function config($stateProvider, $urlRouterProvider) { }; if (route.abstract) configRoute.abstract = true; + + if (!route.state.includes('.')) + configRoute.parent = 'layout'; + if (route.routeParams) configRoute.params = route.routeParams; diff --git a/loopback/locale/en.json b/loopback/locale/en.json index d4695f72c..a406b55a5 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -66,9 +66,10 @@ "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*", "Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})", - "Claim will be picked": "The product from the claim [{{claimId}}]({{{claimUrl}}}) from the client *{{clientName}}* will be picked", - "Claim state has changed to incomplete": "The state of the claim [{{claimId}}]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*", - "Claim state has changed to canceled": "The state of the claim [{{claimId}}]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*", + "Change quantity": "{{concept}} change of {{oldQuantity}} to {{newQuantity}}", + "Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked", + "Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*", + "Claim state has changed to canceled": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*", "Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member", "Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member", "Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}", @@ -140,8 +141,10 @@ "You don't have grant privilege": "You don't have grant privilege", "You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user", "Email verify": "Email verify", - "Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) merged with [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})", + "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", "Client's email was not found": "Client's email was not found" diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 1b0a50433..ea83b36c4 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -134,9 +134,10 @@ "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", - "Claim will be picked": "Se recogerá el género de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}*", - "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*", - "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*", + "Change quantity": "{{concept}} cambia de {{oldQuantity}} a {{newQuantity}}", + "Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*", + "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*", + "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*", "Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}", "ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto", "Distance must be lesser than 1000": "La distancia debe ser inferior a 1000", @@ -241,13 +242,15 @@ "Claim pickup order sent": "Reclamación Orden de recogida enviada [{{claimId}}]({{{claimUrl}}}) al cliente *{{clientName}}*", "You don't have grant privilege": "No tienes privilegios para dar privilegios", "You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario", - "Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) fusionado con [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})", + "Ticket merged": "Ticket [{{originId}}]({{{originFullPath}}}) ({{{originDated}}}) fusionado con [{{destinationId}}]({{{destinationFullPath}}}) ({{{destinationDated}}})", "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/client/back/model-config.json b/modules/client/back/model-config.json index 4ef34ca3a..b466aa5a1 100644 --- a/modules/client/back/model-config.json +++ b/modules/client/back/model-config.json @@ -104,6 +104,9 @@ "SageTransactionType": { "dataSource": "vn" }, + "TicketSms": { + "dataSource": "vn" + }, "TpvError": { "dataSource": "vn" }, diff --git a/modules/client/back/models/ticket-sms.json b/modules/client/back/models/ticket-sms.json new file mode 100644 index 000000000..03f592f51 --- /dev/null +++ b/modules/client/back/models/ticket-sms.json @@ -0,0 +1,28 @@ +{ + "name": "TicketSms", + "base": "VnModel", + "options": { + "mysql": { + "table": "ticketSms" + } + }, + "properties": { + "smsFk": { + "type": "number", + "id": true, + "description": "Identifier" + } + }, + "relations": { + "ticket": { + "type": "belongsTo", + "model": "Ticket", + "foreignKey": "ticketFk" + }, + "sms": { + "type": "belongsTo", + "model": "Sms", + "foreignKey": "smsFk" + } + } +} diff --git a/modules/client/front/descriptor/index.html b/modules/client/front/descriptor/index.html index cad226416..ef5c2997f 100644 --- a/modules/client/front/descriptor/index.html +++ b/modules/client/front/descriptor/index.html @@ -113,10 +113,11 @@
- - + diff --git a/modules/client/front/descriptor/index.js b/modules/client/front/descriptor/index.js index 4a0d1cd2a..4d8d70edf 100644 --- a/modules/client/front/descriptor/index.js +++ b/modules/client/front/descriptor/index.js @@ -39,6 +39,11 @@ class Controller extends Descriptor { }; this.$.sms.open(); } + + onSmsSend(sms) { + return this.$http.post(`Clients/${this.id}/sendSms`, sms) + .then(() => this.vnApp.showSuccess(this.$t('SMS sent'))); + } } ngModule.vnComponent('vnClientDescriptor', { diff --git a/modules/client/front/index.js b/modules/client/front/index.js index a5782c789..ff767bc9e 100644 --- a/modules/client/front/index.js +++ b/modules/client/front/index.js @@ -35,7 +35,6 @@ import './sample/index'; import './sample/create'; import './web-payment'; import './log'; -import './sms'; import './postcode'; import './postcode/province'; import './postcode/city'; diff --git a/modules/client/front/sms/index.js b/modules/client/front/sms/index.js deleted file mode 100644 index 701ee39af..000000000 --- a/modules/client/front/sms/index.js +++ /dev/null @@ -1,49 +0,0 @@ -import ngModule from '../module'; -import Section from 'salix/components/section'; -import './style.scss'; - -class Controller extends Section { - open() { - this.$.SMSDialog.show(); - } - - charactersRemaining() { - const element = this.$.message; - const value = element.input.value; - - const maxLength = 160; - const textAreaLength = new Blob([value]).size; - return maxLength - textAreaLength; - } - - onResponse() { - try { - if (!this.sms.destination) - throw new Error(`The destination can't be empty`); - if (!this.sms.message) - throw new Error(`The message can't be empty`); - if (this.charactersRemaining() < 0) - throw new Error(`The message it's too long`); - - this.$http.post(`Clients/${this.$params.id}/sendSms`, this.sms).then(res => { - this.vnApp.showMessage(this.$t('SMS sent!')); - - if (res.data) this.emit('send', {response: res.data}); - }); - } catch (e) { - this.vnApp.showError(this.$t(e.message)); - return false; - } - return true; - } -} - -Controller.$inject = ['$element', '$scope', '$http', '$translate', 'vnApp']; - -ngModule.vnComponent('vnClientSms', { - template: require('./index.html'), - controller: Controller, - bindings: { - sms: '<', - } -}); diff --git a/modules/client/front/sms/index.spec.js b/modules/client/front/sms/index.spec.js deleted file mode 100644 index 793c80d6e..000000000 --- a/modules/client/front/sms/index.spec.js +++ /dev/null @@ -1,74 +0,0 @@ -import './index'; - -describe('Client', () => { - describe('Component vnClientSms', () => { - let controller; - let $httpBackend; - let $element; - - beforeEach(ngModule('client')); - - beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { - $httpBackend = _$httpBackend_; - let $scope = $rootScope.$new(); - $element = angular.element(''); - controller = $componentController('vnClientSms', {$element, $scope}); - controller.client = {id: 1101}; - controller.$params = {id: 1101}; - controller.$.message = { - input: { - value: 'My SMS' - } - }; - })); - - describe('onResponse()', () => { - it('should perform a POST query and show a success snackbar', () => { - let params = {destinationFk: 1101, destination: 111111111, message: 'My SMS'}; - controller.sms = {destinationFk: 1101, destination: 111111111, message: 'My SMS'}; - - jest.spyOn(controller.vnApp, 'showMessage'); - $httpBackend.expect('POST', `Clients/1101/sendSms`, params).respond(200, params); - - controller.onResponse(); - $httpBackend.flush(); - - expect(controller.vnApp.showMessage).toHaveBeenCalledWith('SMS sent!'); - }); - - it('should call onResponse without the destination and show an error snackbar', () => { - controller.sms = {destinationFk: 1101, message: 'My SMS'}; - - jest.spyOn(controller.vnApp, 'showError'); - - controller.onResponse('accept'); - - expect(controller.vnApp.showError).toHaveBeenCalledWith(`The destination can't be empty`); - }); - - it('should call onResponse without the message and show an error snackbar', () => { - controller.sms = {destinationFk: 1101, destination: 222222222}; - - jest.spyOn(controller.vnApp, 'showError'); - - controller.onResponse('accept'); - - expect(controller.vnApp.showError).toHaveBeenCalledWith(`The message can't be empty`); - }); - }); - - describe('charactersRemaining()', () => { - it('should return the characters remaining in a element', () => { - controller.$.message = { - input: { - value: 'My message 0€' - } - }; - - let result = controller.charactersRemaining(); - - expect(result).toEqual(145); - }); - }); - }); -}); diff --git a/modules/entry/front/buy/index/index.js b/modules/entry/front/buy/index/index.js index 6d9ee5760..322b81d4e 100644 --- a/modules/entry/front/buy/index/index.js +++ b/modules/entry/front/buy/index/index.js @@ -73,6 +73,12 @@ export default class Controller extends Section { this.vnApp.showSuccess(this.$t('Data saved!')); }); } + + itemSearchFunc($search) { + return /^\d+$/.test($search) + ? {id: $search} + : {name: {like: '%' + $search + '%'}}; + } } ngModule.vnComponent('vnEntryBuyIndex', { diff --git a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js index 72a00b764..fe005f1ab 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js +++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js @@ -9,31 +9,43 @@ module.exports = Self => { accepts: [ { arg: 'ids', - type: ['number'], - description: 'The invoice ids' + type: 'string', + description: 'The invoices ids', + } + ], + returns: [ + { + arg: 'body', + type: 'file', + root: true + }, { + arg: 'Content-Type', + type: 'string', + http: {target: 'header'} + }, { + arg: 'Content-Disposition', + type: 'string', + http: {target: 'header'} } ], - returns: { - arg: 'base64', - type: 'string', - root: true - }, http: { path: '/downloadZip', - verb: 'POST' + verb: 'GET' } }); Self.downloadZip = async function(ctx, ids, options) { const models = Self.app.models; const myOptions = {}; + const zip = new JSZip(); if (typeof options == 'object') Object.assign(myOptions, options); - const zip = new JSZip(); - let totalSize = 0; const zipConfig = await models.ZipConfig.findOne(null, myOptions); + let totalSize = 0; + ids = ids.split(','); + for (let id of ids) { if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large'); const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions); @@ -44,8 +56,10 @@ module.exports = Self => { totalSize += sizeInMegabytes; zip.file(fileName, body); } - const base64 = await zip.generateAsync({type: 'base64'}); - return base64; + + const stream = zip.generateNodeStream({streamFiles: true}); + + return [stream, 'application/zip', `filename="download.zip"`]; }; function extractFileName(str) { diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js index e458ad9ff..4d1833635 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js @@ -3,7 +3,7 @@ const UserError = require('vn-loopback/util/user-error'); describe('InvoiceOut downloadZip()', () => { const userId = 9; - const invoiceIds = [1, 2]; + const invoiceIds = '1,2'; const ctx = { req: { diff --git a/modules/invoiceOut/front/index/index.js b/modules/invoiceOut/front/index/index.js index a46060073..2cde3c940 100644 --- a/modules/invoiceOut/front/index/index.js +++ b/modules/invoiceOut/front/index/index.js @@ -29,13 +29,13 @@ export default class Controller extends Section { window.open(url, '_blank'); } else { const invoiceOutIds = this.checked; - const params = { - ids: invoiceOutIds - }; - this.$http.post(`InvoiceOuts/downloadZip`, params) - .then(res => { - location.href = 'data:application/zip;base64,' + res.data; - }); + const invoicesIds = invoiceOutIds.join(','); + const serializedParams = this.$httpParamSerializer({ + access_token: this.vnToken.token, + ids: invoicesIds + }); + const url = `api/InvoiceOuts/downloadZip?${serializedParams}`; + window.open(url, '_blank'); } } } 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 @@ - { } }); - it('should return the tickets matching the OK State in destination date', async () => { + it('should return the tickets matching the OK State in destination date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, - tfState: "OK" + futureState: 'OK' }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(4); @@ -273,22 +152,20 @@ describe('TicketFuture getTicketsFuture()', () => { } }); - it('should return the tickets matching the correct IPT in origin date', async () => { + it('should return the tickets matching the correct IPT in origin date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, ipt: null }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(4); @@ -300,22 +177,20 @@ describe('TicketFuture getTicketsFuture()', () => { } }); - it('should return the tickets matching the incorrect IPT in origin date', async () => { + it('should return the tickets matching the incorrect IPT in origin date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, ipt: 0 }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(0); @@ -327,22 +202,20 @@ describe('TicketFuture getTicketsFuture()', () => { } }); - it('should return the tickets matching the correct IPT in destination date', async () => { + it('should return the tickets matching the correct IPT in destination date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, - tfIpt: null + futureIpt: null }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(4); @@ -354,22 +227,20 @@ describe('TicketFuture getTicketsFuture()', () => { } }); - it('should return the tickets matching the incorrect IPT in destination date', async () => { + it('should return the tickets matching the incorrect IPT in destination date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, - tfIpt: 0 + futureIpt: 0 }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(0); @@ -381,22 +252,20 @@ describe('TicketFuture getTicketsFuture()', () => { } }); - it('should return the tickets matching the ID in origin date', async () => { + it('should return the tickets matching the ID in origin date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, id: 13 }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(1); @@ -408,22 +277,20 @@ describe('TicketFuture getTicketsFuture()', () => { } }); - it('should return the tickets matching the ID in destination date', async () => { + it('should return the tickets matching the ID in destination date', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const args = { originDated: today, futureDated: today, - litersMax: 9999, - linesMax: 9999, warehouseFk: 1, - tfId: 12 + futureId: 12 }; - const ctx = { req: { accessToken: { userId: 9 } }, args }; + const ctx = {req: {accessToken: {userId: 9}}, args}; const result = await models.Ticket.getTicketsFuture(ctx, options); expect(result.length).toEqual(4); @@ -434,5 +301,4 @@ describe('TicketFuture getTicketsFuture()', () => { throw e; } }); - }); diff --git a/modules/ticket/back/methods/ticket/specs/merge.spec.js b/modules/ticket/back/methods/ticket/specs/merge.spec.js index 713f86ad6..275484f67 100644 --- a/modules/ticket/back/methods/ticket/specs/merge.spec.js +++ b/modules/ticket/back/methods/ticket/specs/merge.spec.js @@ -3,15 +3,15 @@ const LoopBackContext = require('loopback-context'); describe('ticket merge()', () => { const tickets = [{ - id: 13, - ticketFuture: 12, - workerFk: 1, - originETD: new Date(), - destETD: new Date() + originId: 13, + destinationId: 12, + originShipped: new Date(), + destinationShipped: new Date(), + workerFk: 1 }]; const activeCtx = { - accessToken: { userId: 9 }, + accessToken: {userId: 9}, }; beforeEach(() => { @@ -22,26 +22,26 @@ describe('ticket merge()', () => { const ctx = { req: { - accessToken: { userId: 9 }, - headers: { origin: 'http://localhost:5000' }, + accessToken: {userId: 9}, + headers: {origin: 'http://localhost:5000'}, } }; ctx.req.__ = value => { return value; }; - it('should merge two tickets', async () => { + it('should merge two tickets', async() => { const tx = await models.Ticket.beginTransaction({}); try { - const options = { transaction: tx }; + const options = {transaction: tx}; const chatNotificationBeforeMerge = await models.Chat.find(); await models.Ticket.merge(ctx, tickets, options); - const createdTicketLog = await models.TicketLog.find({ where: { originFk: tickets[0].id } }, options); - const deletedTicket = await models.Ticket.findOne({ where: { id: tickets[0].id } }, options); - const salesTicketFuture = await models.Sale.find({ where: { ticketFk: tickets[0].ticketFuture } }, options); + const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets[0].originId}}, options); + const deletedTicket = await models.Ticket.findOne({where: {id: tickets[0].originId}}, options); + const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets[0].destinationId}}, options); const chatNotificationAfterMerge = await models.Chat.find(); expect(createdTicketLog.length).toEqual(1); diff --git a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js index 96d29c46f..5470382f9 100644 --- a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js +++ b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js @@ -87,7 +87,7 @@ describe('sale priceDifference()', () => { const secondtItem = result.items[1]; expect(firstItem.movable).toEqual(410); - expect(secondtItem.movable).toEqual(1810); + expect(secondtItem.movable).toEqual(1790); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/methods/ticket/specs/sendSms.spec.js b/modules/ticket/back/methods/ticket/specs/sendSms.spec.js index f50253b10..f94b8be2a 100644 --- a/modules/ticket/back/methods/ticket/specs/sendSms.spec.js +++ b/modules/ticket/back/methods/ticket/specs/sendSms.spec.js @@ -15,9 +15,16 @@ describe('ticket sendSms()', () => { const sms = await models.Ticket.sendSms(ctx, id, destination, message, options); const createdLog = await models.TicketLog.findById(sms.logId, null, options); + + const filter = { + ticketFk: createdLog.originFk + }; + const ticketSms = await models.TicketSms.findOne(filter, options); + const json = JSON.parse(JSON.stringify(createdLog.newInstance)); expect(json.message).toEqual(message); + expect(ticketSms.ticketFk).toEqual(createdLog.originFk); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/model-config.json b/modules/ticket/back/model-config.json index baaca595e..50cfbd08a 100644 --- a/modules/ticket/back/model-config.json +++ b/modules/ticket/back/model-config.json @@ -32,9 +32,6 @@ "Sale": { "dataSource": "vn" }, - "SaleChecked": { - "dataSource": "vn" - }, "SaleCloned": { "dataSource": "vn" }, @@ -44,7 +41,7 @@ "SaleTracking": { "dataSource": "vn" }, - "State":{ + "State": { "dataSource": "vn" }, "Ticket": { @@ -71,16 +68,16 @@ "TicketRequest": { "dataSource": "vn" }, - "TicketState":{ + "TicketState": { "dataSource": "vn" }, - "TicketLastState":{ + "TicketLastState": { "dataSource": "vn" }, - "TicketService":{ + "TicketService": { "dataSource": "vn" }, - "TicketServiceType":{ + "TicketServiceType": { "dataSource": "vn" }, "TicketTracking": { @@ -94,8 +91,5 @@ }, "TicketConfig": { "dataSource": "vn" - }, - "TicketFuture": { - "dataSource": "vn" } } diff --git a/modules/ticket/back/models/sale-checked.json b/modules/ticket/back/models/sale-checked.json deleted file mode 100644 index 96d790505..000000000 --- a/modules/ticket/back/models/sale-checked.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "SaleChecked", - "base": "VnModel", - "options": { - "mysql": { - "table": "saleChecked" - } - }, - "properties": { - "isChecked": { - "type": "number" - }, - "saleFk": { - "id": true - } - }, - "relations": { - "sale": { - "type": "belongsTo", - "model": "Sale", - "foreignKey": "saleFk" - } - } -} \ No newline at end of file diff --git a/modules/ticket/back/models/sale.json b/modules/ticket/back/models/sale.json index e18f0291f..b30954ad1 100644 --- a/modules/ticket/back/models/sale.json +++ b/modules/ticket/back/models/sale.json @@ -56,11 +56,6 @@ "foreignKey": "ticketFk", "required": true }, - "isChecked": { - "type": "hasOne", - "model": "SaleChecked", - "foreignKey": "saleFk" - }, "components": { "type": "hasMany", "model": "SaleComponent", @@ -80,6 +75,6 @@ "type": "hasOne", "model": "ItemShelvingSale", "foreignKey": "saleFk" - } + } } } diff --git a/modules/ticket/back/models/ticket-future.json b/modules/ticket/back/models/ticket-future.json deleted file mode 100644 index 00277ab8a..000000000 --- a/modules/ticket/back/models/ticket-future.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "TicketFuture", - "base": "PersistedModel", - "acls": [ - { - "accessType": "READ", - "principalType": "ROLE", - "principalId": "employee", - "permission": "ALLOW" - } - ] - } diff --git a/modules/ticket/back/models/ticket-log.js b/modules/ticket/back/models/ticket-log.js new file mode 100644 index 000000000..81855ada7 --- /dev/null +++ b/modules/ticket/back/models/ticket-log.js @@ -0,0 +1,3 @@ +module.exports = function(Self) { + require('../methods/ticket-log/getChanges')(Self); +}; diff --git a/modules/ticket/back/models/ticket-methods.js b/modules/ticket/back/models/ticket-methods.js index 8fd74d35c..73df0579c 100644 --- a/modules/ticket/back/models/ticket-methods.js +++ b/modules/ticket/back/models/ticket-methods.js @@ -33,8 +33,9 @@ module.exports = function(Self) { require('../methods/ticket/closeByTicket')(Self); require('../methods/ticket/closeByAgency')(Self); require('../methods/ticket/closeByRoute')(Self); - require('../methods/ticket-future/getTicketsFuture')(Self); + require('../methods/ticket/getTicketsFuture')(Self); require('../methods/ticket/merge')(Self); + require('../methods/ticket/getTicketsAdvance')(Self); require('../methods/ticket/isRoleAdvanced')(Self); require('../methods/ticket/collectionLabel')(Self); require('../methods/ticket/expeditionPalletLabel')(Self); diff --git a/modules/ticket/front/advance-search-panel/index.html b/modules/ticket/front/advance-search-panel/index.html new file mode 100644 index 000000000..e8d5dc60d --- /dev/null +++ b/modules/ticket/front/advance-search-panel/index.html @@ -0,0 +1,76 @@ +
+
+ + + + + + + + + + {{description}} + + + + + {{description}} + + + + + + + {{name}} + + + + + {{name}} + + + + + + + + + + +
+
diff --git a/modules/ticket/front/advance-search-panel/index.js b/modules/ticket/front/advance-search-panel/index.js new file mode 100644 index 000000000..259a40931 --- /dev/null +++ b/modules/ticket/front/advance-search-panel/index.js @@ -0,0 +1,43 @@ +import ngModule from '../module'; +import SearchPanel from 'core/components/searchbar/search-panel'; + +class Controller extends SearchPanel { + constructor($, $element) { + super($, $element); + this.filter = this.$.filter; + this.getGroupedStates(); + this.getItemPackingTypes(); + } + + getGroupedStates() { + let groupedStates = []; + this.$http.get('AlertLevels').then(res => { + for (let state of res.data) { + groupedStates.push({ + id: state.id, + code: state.code, + name: this.$t(state.code) + }); + } + this.groupedStates = groupedStates; + }); + } + + getItemPackingTypes() { + let itemPackingTypes = []; + this.$http.get('ItemPackingTypes').then(res => { + for (let ipt of res.data) { + itemPackingTypes.push({ + code: ipt.code, + description: this.$t(ipt.description) + }); + } + this.itemPackingTypes = itemPackingTypes; + }); + } +} + +ngModule.vnComponent('vnAdvanceTicketSearchPanel', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/ticket/front/advance-search-panel/locale/en.yml b/modules/ticket/front/advance-search-panel/locale/en.yml new file mode 100644 index 000000000..f01932c7a --- /dev/null +++ b/modules/ticket/front/advance-search-panel/locale/en.yml @@ -0,0 +1 @@ +Advance tickets: Advance tickets diff --git a/modules/ticket/front/advance-search-panel/locale/es.yml b/modules/ticket/front/advance-search-panel/locale/es.yml new file mode 100644 index 000000000..3dce7dae5 --- /dev/null +++ b/modules/ticket/front/advance-search-panel/locale/es.yml @@ -0,0 +1 @@ +Advance tickets: Adelantar tickets diff --git a/modules/ticket/front/advance/index.html b/modules/ticket/front/advance/index.html new file mode 100644 index 000000000..f63c0fbf7 --- /dev/null +++ b/modules/ticket/front/advance/index.html @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OriginDestination
+ + + + ID + + Date + + IPT + + State + + Import + + ID + + Date + + IPT + + State + + Liters + + Stock + + Lines + + Import +
+ + + + + {{::ticket.futureId | dashIfEmpty}} + + + + {{::ticket.futureShipped | date: 'dd/MM/yyyy'}} + + {{::ticket.futureIpt | dashIfEmpty}} + + {{::ticket.futureState | dashIfEmpty}} + + + + {{::(ticket.futureTotalWithVat ? ticket.futureTotalWithVat : 0) | currency: 'EUR': 2}} + + + + {{::ticket.id | dashIfEmpty}} + + + + {{::ticket.shipped | date: 'dd/MM/yyyy'}} + + {{::ticket.ipt | dashIfEmpty}} + + {{::ticket.state | dashIfEmpty}} + + {{::ticket.liters | dashIfEmpty}}{{::ticket.hasStock | dashIfEmpty}}{{::ticket.lines | dashIfEmpty}} + + {{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}} + +
+
+
+
+ + + + diff --git a/modules/ticket/front/advance/index.js b/modules/ticket/front/advance/index.js new file mode 100644 index 000000000..1404f9472 --- /dev/null +++ b/modules/ticket/front/advance/index.js @@ -0,0 +1,177 @@ +import ngModule from '../module'; +import Section from 'salix/components/section'; + +export default class Controller extends Section { + constructor($element, $) { + super($element, $); + this.$checkAll = false; + + this.smartTableOptions = { + activeButtons: { + search: true, + }, + columns: [ + { + field: 'state', + searchable: false + }, + { + field: 'futureState', + searchable: false + }, + { + field: 'totalWithVat', + searchable: false + }, + { + field: 'futureTotalWithVat', + searchable: false + }, + { + field: 'shipped', + searchable: false + }, + { + field: 'futureShipped', + searchable: false + }, + { + field: 'ipt', + autocomplete: { + url: 'ItemPackingTypes', + showField: 'description', + valueField: 'code' + } + }, + { + field: 'futureIpt', + autocomplete: { + url: 'ItemPackingTypes', + showField: 'description', + valueField: 'code' + } + }, + ] + }; + } + + $postLink() { + this.setDefaultFilter(); + } + + setDefaultFilter() { + let today = new Date(); + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); + this.filterParams = { + dateFuture: tomorrow, + dateToAdvance: today, + warehouseFk: this.vnConfig.warehouseFk + }; + this.$.model.applyFilter(null, this.filterParams); + } + + compareDate(date) { + let today = new Date(); + today.setHours(0, 0, 0, 0); + let timeTicket = new Date(date); + timeTicket.setHours(0, 0, 0, 0); + + let comparation = today - timeTicket; + + if (comparation == 0) + return 'warning'; + if (comparation < 0) + return 'success'; + } + + get checked() { + const tickets = this.$.model.data || []; + const checkedLines = []; + for (let ticket of tickets) { + if (ticket.checked) + checkedLines.push(ticket); + } + + return checkedLines; + } + + stateColor(state) { + if (state === 'OK') + return 'success'; + else if (state === 'Libre') + return 'notice'; + } + + dateRange(value) { + const minHour = new Date(value); + minHour.setHours(0, 0, 0, 0); + const maxHour = new Date(value); + maxHour.setHours(23, 59, 59, 59); + + return [minHour, maxHour]; + } + + totalPriceColor(totalWithVat) { + const total = parseInt(totalWithVat); + if (total > 0 && total < 50) + return 'warning'; + } + + get confirmationMessage() { + if (!this.$.model) return 0; + + return this.$t(`Advance confirmation`, { + checked: this.checked.length + }); + } + + moveTicketsAdvance() { + let ticketsToMove = []; + this.checked.forEach(ticket => { + ticketsToMove.push({ + originId: ticket.futureId, + destinationId: ticket.id, + originShipped: ticket.futureShipped, + destinationShipped: ticket.shipped, + workerFk: ticket.workerFk + }); + }); + const params = {tickets: ticketsToMove}; + return this.$http.post('Tickets/merge', params) + .then(() => { + this.$.model.refresh(); + this.vnApp.showSuccess(this.$t('Success')); + }); + } + + exprBuilder(param, value) { + switch (param) { + case 'id': + return {'id': value}; + case 'futureId': + return {'futureId': value}; + case 'liters': + return {'liters': value}; + case 'lines': + return {'lines': value}; + case 'ipt': + return {'ipt': value}; + case 'futureIpt': + return {'futureIpt': value}; + case 'totalWithVat': + return {'totalWithVat': value}; + case 'futureTotalWithVat': + return {'futureTotalWithVat': value}; + case 'hasStock': + return {'hasStock': value}; + } + } +} + +Controller.$inject = ['$element', '$scope']; + +ngModule.vnComponent('vnTicketAdvance', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/ticket/front/advance/index.spec.js b/modules/ticket/front/advance/index.spec.js new file mode 100644 index 000000000..c5a04daee --- /dev/null +++ b/modules/ticket/front/advance/index.spec.js @@ -0,0 +1,113 @@ +import './index.js'; +import crudModel from 'core/mocks/crud-model'; + +describe('Component vnTicketAdvance', () => { + let controller; + let $httpBackend; + + beforeEach(ngModule('ticket') + ); + + beforeEach(inject(($componentController, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + const $element = angular.element(''); + controller = $componentController('vnTicketAdvance', {$element}); + controller.$.model = crudModel; + controller.$.model.data = [{ + id: 1, + checked: true, + state: 'OK' + }, { + id: 2, + checked: true, + state: 'Libre' + }]; + })); + + describe('compareDate()', () => { + it('should return warning when the date is the present', () => { + let today = new Date(); + let result = controller.compareDate(today); + + expect(result).toEqual('warning'); + }); + + it('should return sucess when the date is in the future', () => { + let futureDate = new Date(); + futureDate = futureDate.setDate(futureDate.getDate() + 10); + let result = controller.compareDate(futureDate); + + expect(result).toEqual('success'); + }); + + it('should return undefined when the date is in the past', () => { + let pastDate = new Date(); + pastDate = pastDate.setDate(pastDate.getDate() - 10); + let result = controller.compareDate(pastDate); + + expect(result).toEqual(undefined); + }); + }); + + describe('checked()', () => { + it('should return an array of checked tickets', () => { + const result = controller.checked; + const firstRow = result[0]; + const secondRow = result[1]; + + expect(result.length).toEqual(2); + expect(firstRow.id).toEqual(1); + expect(secondRow.id).toEqual(2); + }); + }); + + describe('stateColor()', () => { + it('should return success to the OK tickets', () => { + const ok = controller.stateColor(controller.$.model.data[0].state); + const notOk = controller.stateColor(controller.$.model.data[1].state); + + expect(ok).toEqual('success'); + expect(notOk).not.toEqual('success'); + }); + + it('should return success to the FREE tickets', () => { + const notFree = controller.stateColor(controller.$.model.data[0].state); + const free = controller.stateColor(controller.$.model.data[1].state); + + expect(free).toEqual('notice'); + expect(notFree).not.toEqual('notice'); + }); + }); + + describe('dateRange()', () => { + it('should return two dates with the hours at the start and end of the given date', () => { + const now = new Date(); + + const today = now.getDate(); + + const dateRange = controller.dateRange(now); + const start = dateRange[0].toString(); + const end = dateRange[1].toString(); + + expect(start).toContain(today); + expect(start).toContain('00:00:00'); + + expect(end).toContain(today); + expect(end).toContain('23:59:59'); + }); + }); + + describe('moveTicketsAdvance()', () => { + it('should make an HTTP Post query', () => { + jest.spyOn(controller.$.model, 'refresh'); + jest.spyOn(controller.vnApp, 'showSuccess'); + + $httpBackend.expectPOST(`Tickets/merge`).respond(); + controller.moveTicketsAdvance(); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalled(); + expect(controller.$.model.refresh).toHaveBeenCalledWith(); + }); + }); +}); diff --git a/modules/ticket/front/advance/locale/en.yml b/modules/ticket/front/advance/locale/en.yml new file mode 100644 index 000000000..a47d951d0 --- /dev/null +++ b/modules/ticket/front/advance/locale/en.yml @@ -0,0 +1,2 @@ +Advance tickets: Advance tickets +Success: Tickets moved successfully! diff --git a/modules/ticket/front/advance/locale/es.yml b/modules/ticket/front/advance/locale/es.yml new file mode 100644 index 000000000..b444fbdd3 --- /dev/null +++ b/modules/ticket/front/advance/locale/es.yml @@ -0,0 +1,6 @@ +Advance tickets: Adelantar tickets +Search advance tickets by date: Busca tickets para adelantar por fecha +Advance confirmation: ¿Desea adelantar {{checked}} tickets? +Success: Tickets movidos correctamente +Lines: Líneas +Liters: Litros diff --git a/modules/ticket/front/descriptor-menu/index.html b/modules/ticket/front/descriptor-menu/index.html index 0c04b42fb..805e0b391 100644 --- a/modules/ticket/front/descriptor-menu/index.html +++ b/modules/ticket/front/descriptor-menu/index.html @@ -43,7 +43,7 @@ ng-if="!$ctrl.hasDocuwareFile" ng-click="$ctrl.showPdfDeliveryNote('withoutPrices')" translate> - as PDF without prices + as PDF without prices SMS Minimum import + + SMS Notify changes + @@ -251,13 +257,13 @@ - - - - + - \ No newline at end of file + diff --git a/modules/ticket/front/descriptor-menu/index.js b/modules/ticket/front/descriptor-menu/index.js index 100d27cd0..168002d07 100644 --- a/modules/ticket/front/descriptor-menu/index.js +++ b/modules/ticket/front/descriptor-menu/index.js @@ -225,6 +225,18 @@ class Controller extends Section { }); } + sendChangesSms() { + return this.$http.get(`TicketLogs/${this.id}/getChanges`) + .then(res => { + const params = { + ticketId: this.id, + created: this.ticket.updated, + changes: res.data + }; + this.showSMSDialog({message: this.$t('Send changes', params)}); + }); + } + showSMSDialog(params) { const address = this.ticket.address; const client = this.ticket.client; @@ -239,6 +251,7 @@ class Controller extends Section { destinationFk: this.ticket.clientFk, destination: phone }, params); + this.$.sms.open(); } @@ -294,6 +307,11 @@ class Controller extends Section { this.$state.go('ticket.card.sale', {id: refundTicket.id}); }); } + + onSmsSend(sms) { + return this.$http.post(`Tickets/${this.id}/sendSms`, sms) + .then(() => this.vnApp.showSuccess(this.$t('SMS sent'))); + } } Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail']; diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js index 1716e36f6..48b64f4a0 100644 --- a/modules/ticket/front/descriptor-menu/index.spec.js +++ b/modules/ticket/front/descriptor-menu/index.spec.js @@ -258,14 +258,24 @@ describe('Ticket Component vnTicketDescriptorMenu', () => { }); }); - describe('showSMSDialog()', () => { - it('should set the destionationFk and destination properties and then call the sms open() method', () => { + describe('sendChangesSms()', () => { + it('should make a query and open the sms dialog', () => { controller.$.sms = {open: () => {}}; jest.spyOn(controller.$.sms, 'open'); - controller.showSMSDialog(); + $httpBackend.expectGET(`TicketLogs/${ticket.id}/getChanges`).respond(); + controller.sendChangesSms(); + $httpBackend.flush(); expect(controller.$.sms.open).toHaveBeenCalledWith(); + }); + }); + + describe('showSMSDialog()', () => { + it('should set the destionationFk and destination properties and then call the sms open() method', () => { + controller.$.sms = {open: () => {}}; + controller.showSMSDialog(); + expect(controller.newSMS).toEqual({ destinationFk: ticket.clientFk, destination: ticket.address.mobile, diff --git a/modules/ticket/front/descriptor-menu/locale/es.yml b/modules/ticket/front/descriptor-menu/locale/es.yml index a09a32131..a2725f485 100644 --- a/modules/ticket/front/descriptor-menu/locale/es.yml +++ b/modules/ticket/front/descriptor-menu/locale/es.yml @@ -11,4 +11,5 @@ Show Proforma: Ver proforma Refund all: Abonar todo Invoice sent: Factura enviada The following refund ticket have been created: "Se ha creado siguiente ticket de abono: {{ticketId}}" -Transfer client: Transferir cliente \ No newline at end of file +Transfer client: Transferir cliente +SMS Notify changes: SMS Notificar cambios diff --git a/modules/ticket/front/descriptor/locale/en.yml b/modules/ticket/front/descriptor/locale/en.yml index 64075c7ef..8eed2265d 100644 --- a/modules/ticket/front/descriptor/locale/en.yml +++ b/modules/ticket/front/descriptor/locale/en.yml @@ -1,2 +1,3 @@ Make a payment: "Verdnatura communicates:\rYour order is pending of payment.\rPlease, enter the web page and make the payment with card.\rThank you." Minimum is needed: "Verdnatura communicates:\rA minimum import of 50€ (Without BAT) is needed for your order {{ticketId}} from date {{created | date: 'dd/MM/yyyy'}} to receive it with no extra fees." +Send changes: "Verdnatura communicates:\rOrder {{ticketId}} date {{created | date: 'dd/MM/yyyy'}}\r{{changes}}" diff --git a/modules/ticket/front/descriptor/locale/es.yml b/modules/ticket/front/descriptor/locale/es.yml index bce9e62d7..d921b5dc2 100644 --- a/modules/ticket/front/descriptor/locale/es.yml +++ b/modules/ticket/front/descriptor/locale/es.yml @@ -23,3 +23,4 @@ Restore ticket: Restaurar ticket You are going to restore this ticket: Vas a restaurar este ticket Are you sure you want to restore this ticket?: ¿Seguro que quieres restaurar el ticket? Are you sure you want to refund all?: ¿Seguro que quieres abonar todo? +Send changes: "Verdnatura le recuerda:\rPedido {{ticketId}} día {{created | date: 'dd/MM/yyyy'}}\r{{changes}}" diff --git a/modules/ticket/front/future-search-panel/index.html b/modules/ticket/front/future-search-panel/index.html index 1b3ae453e..18b574f2a 100644 --- a/modules/ticket/front/future-search-panel/index.html +++ b/modules/ticket/front/future-search-panel/index.html @@ -4,43 +4,26 @@ + ng-model="filter.originDated" + required="true"> - - - - - - + required="true"> - - + ng-model="filter.litersMax"> + + @@ -48,22 +31,22 @@ data="$ctrl.itemPackingTypes" label="Origin IPT" value-field="code" - show-field="name" + show-field="description" ng-model="filter.ipt" info="IPT"> - {{name}} + {{description}} - {{name}} + {{description}} @@ -83,7 +66,7 @@ label="Destination Grouped State" value-field="code" show-field="name" - ng-model="filter.tfState"> + ng-model="filter.futureState"> {{name}} diff --git a/modules/ticket/front/future-search-panel/index.js b/modules/ticket/front/future-search-panel/index.js index 1a1f0e4c5..d7e7b3a5e 100644 --- a/modules/ticket/front/future-search-panel/index.js +++ b/modules/ticket/front/future-search-panel/index.js @@ -28,9 +28,8 @@ class Controller extends SearchPanel { this.$http.get('ItemPackingTypes').then(res => { for (let ipt of res.data) { itemPackingTypes.push({ - id: ipt.id, + description: this.$t(ipt.description), code: ipt.code, - name: this.$t(ipt.code) }); } this.itemPackingTypes = itemPackingTypes; diff --git a/modules/ticket/front/future-search-panel/locale/en.yml b/modules/ticket/front/future-search-panel/locale/en.yml index fe71865cb..767c20152 100644 --- a/modules/ticket/front/future-search-panel/locale/en.yml +++ b/modules/ticket/front/future-search-panel/locale/en.yml @@ -1,9 +1 @@ Future tickets: Tickets a futuro -FREE: Free -DELIVERED: Delivered -ON_PREPARATION: On preparation -PACKED: Packed -F: Fruits and vegetables -V: Vertical -H: Horizontal -P: Feed diff --git a/modules/ticket/front/future-search-panel/locale/es.yml b/modules/ticket/front/future-search-panel/locale/es.yml index 82deba538..9d72c5b06 100644 --- a/modules/ticket/front/future-search-panel/locale/es.yml +++ b/modules/ticket/front/future-search-panel/locale/es.yml @@ -11,13 +11,4 @@ With problems: Con problemas Warehouse: Almacén Origin Grouped State: Estado agrupado origen Destination Grouped State: Estado agrupado destino -FREE: Libre -DELIVERED: Servido -ON_PREPARATION: En preparacion -PACKED: Encajado -F: Frutas y verduras -V: Vertical -H: Horizontal -P: Pienso -ETD: Tiempo estimado de entrega IPT: Encajado diff --git a/modules/ticket/front/future/index.html b/modules/ticket/front/future/index.html index d30cbaf19..1af1fb9ba 100644 --- a/modules/ticket/front/future/index.html +++ b/modules/ticket/front/future/index.html @@ -1,7 +1,7 @@ + auto-load="false"> + + + + + - - + - - - - - - - + @@ -125,38 +130,38 @@ {{::ticket.id}} + - + -
OriginDestination
+ Problems - Origin ID + ID - Origin ETD + + Date + + IPT - Origin State + State - IPT - + Liters + Available Lines - Destination ID + + ID - Destination ETD + + Date - Destination State - + IPT + State +
- - {{::ticket.originETD | date: 'dd/MM/yyyy'}} + + {{::ticket.shipped | date: 'dd/MM/yyyy'}} {{::ticket.ipt}} {{::ticket.state}} {{::ticket.ipt}} {{::ticket.liters}} {{::ticket.lines}} - {{::ticket.ticketFuture}} + {{::ticket.futureId}} - - {{::ticket.destETD | date: 'dd/MM/yyyy'}} + + {{::ticket.futureShipped | date: 'dd/MM/yyyy'}} {{::ticket.futureIpt}} - {{::ticket.tfState}} + class="chip {{$ctrl.stateColor(ticket.futureState)}}"> + {{::ticket.futureState}} {{::ticket.tfIpt}}
diff --git a/modules/ticket/front/future/index.js b/modules/ticket/front/future/index.js index 311b9c307..56ba1608e 100644 --- a/modules/ticket/front/future/index.js +++ b/modules/ticket/front/future/index.js @@ -11,15 +11,15 @@ export default class Controller extends Section { search: true, }, columns: [{ - field: 'problems', + field: 'totalProblems', + searchable: false, + }, + { + field: 'shipped', searchable: false }, { - field: 'originETD', - searchable: false - }, - { - field: 'destETD', + field: 'futureShipped', searchable: false }, { @@ -27,7 +27,7 @@ export default class Controller extends Section { searchable: false }, { - field: 'tfState', + field: 'futureState', searchable: false }, { @@ -39,7 +39,7 @@ export default class Controller extends Section { } }, { - field: 'tfIpt', + field: 'futureIpt', autocomplete: { url: 'ItemPackingTypes', showField: 'description', @@ -48,6 +48,9 @@ export default class Controller extends Section { }, ] }; + } + + $postLink() { this.setDefaultFilter(); } @@ -57,10 +60,9 @@ export default class Controller extends Section { this.filterParams = { originDated: today, futureDated: today, - linesMax: '9999', - litersMax: '9999', - warehouseFk: 1 + warehouseFk: this.vnConfig.warehouseFk }; + this.$.model.applyFilter(null, this.filterParams); } compareDate(date) { @@ -113,7 +115,17 @@ export default class Controller extends Section { } moveTicketsFuture() { - let params = { tickets: this.checked }; + let ticketsToMove = []; + this.checked.forEach(ticket => { + ticketsToMove.push({ + originId: ticket.id, + destinationId: ticket.futureId, + originShipped: ticket.shipped, + destinationShipped: ticket.futureShipped, + workerFk: ticket.workerFk + }); + }); + let params = {tickets: ticketsToMove}; return this.$http.post('Tickets/merge', params) .then(() => { this.$.model.refresh(); @@ -123,18 +135,18 @@ export default class Controller extends Section { exprBuilder(param, value) { switch (param) { - case 'id': - return { 'id': value }; - case 'ticketFuture': - return { 'ticketFuture': value }; - case 'litersMax': - return { 'liters': value }; - case 'linesMax': - return { 'lines': value }; - case 'ipt': - return { 'ipt': value }; - case 'tfIpt': - return { 'tfIpt': value }; + case 'id': + return {'id': value}; + case 'futureId': + return {'futureId': value}; + case 'liters': + return {'liters': value}; + case 'lines': + return {'lines': value}; + case 'ipt': + return {'ipt': value}; + case 'futureIpt': + return {'futureIpt': value}; } } } diff --git a/modules/ticket/front/future/index.spec.js b/modules/ticket/front/future/index.spec.js index 63deebc4f..c609a4891 100644 --- a/modules/ticket/front/future/index.spec.js +++ b/modules/ticket/front/future/index.spec.js @@ -2,33 +2,30 @@ import './index.js'; import crudModel from 'core/mocks/crud-model'; describe('Component vnTicketFuture', () => { + const today = new Date(); let controller; let $httpBackend; - let $window; - beforeEach(ngModule('ticket') - ); + beforeEach(ngModule('ticket')); - beforeEach(inject(($componentController, _$window_, _$httpBackend_) => { + beforeEach(inject(($componentController, _$httpBackend_) => { $httpBackend = _$httpBackend_; - $window = _$window_; const $element = angular.element(''); - controller = $componentController('vnTicketFuture', { $element }); + controller = $componentController('vnTicketFuture', {$element}); controller.$.model = crudModel; controller.$.model.data = [{ id: 1, checked: true, - state: "OK" + state: 'OK' }, { id: 2, checked: true, - state: "Libre" + state: 'Libre' }]; })); describe('compareDate()', () => { it('should return warning when the date is the present', () => { - let today = new Date(); let result = controller.compareDate(today); expect(result).toEqual('warning'); @@ -67,6 +64,7 @@ describe('Component vnTicketFuture', () => { it('should return success to the OK tickets', () => { const ok = controller.stateColor(controller.$.model.data[0].state); const notOk = controller.stateColor(controller.$.model.data[1].state); + expect(ok).toEqual('success'); expect(notOk).not.toEqual('success'); }); @@ -74,6 +72,7 @@ describe('Component vnTicketFuture', () => { it('should return success to the FREE tickets', () => { const notFree = controller.stateColor(controller.$.model.data[0].state); const free = controller.stateColor(controller.$.model.data[1].state); + expect(free).toEqual('notice'); expect(notFree).not.toEqual('notice'); }); @@ -81,18 +80,14 @@ describe('Component vnTicketFuture', () => { describe('dateRange()', () => { it('should return two dates with the hours at the start and end of the given date', () => { - const now = new Date(); - - const today = now.getDate(); - - const dateRange = controller.dateRange(now); + const dateRange = controller.dateRange(today); const start = dateRange[0].toString(); const end = dateRange[1].toString(); - expect(start).toContain(today); + expect(start).toContain(today.getDate()); expect(start).toContain('00:00:00'); - expect(end).toContain(today); + expect(end).toContain(today.getDate()); expect(end).toContain('23:59:59'); }); }); diff --git a/modules/ticket/front/future/locale/en.yml b/modules/ticket/front/future/locale/en.yml index 66d3ce269..4400e6992 100644 --- a/modules/ticket/front/future/locale/en.yml +++ b/modules/ticket/front/future/locale/en.yml @@ -1,6 +1,2 @@ Move confirmation: Do you want to move {{checked}} tickets to the future? -FREE: Free -DELIVERED: Delivered -ON_PREPARATION: On preparation -PACKED: Packed Success: Tickets moved successfully! diff --git a/modules/ticket/front/future/locale/es.yml b/modules/ticket/front/future/locale/es.yml index 9be0be6a4..9fceea111 100644 --- a/modules/ticket/front/future/locale/es.yml +++ b/modules/ticket/front/future/locale/es.yml @@ -3,20 +3,14 @@ Search tickets: Buscar tickets Search future tickets by date: Buscar tickets por fecha Problems: Problemas Origin ID: ID origen -Closing: Cierre Origin State: Estado origen Destination State: Estado destino Liters: Litros Available Lines: Líneas disponibles Destination ID: ID destino -Destination ETD: ETD Destino -Origin ETD: ETD Origen Move tickets: Mover tickets Move confirmation: ¿Desea mover {{checked}} tickets hacia el futuro? Success: Tickets movidos correctamente -ETD: Tiempo estimado de entrega IPT: Encajado -FREE: Libre -DELIVERED: Servido -ON_PREPARATION: En preparacion -PACKED: Encajado +Origin Date: Fecha origen +Destination Date: Fecha destino diff --git a/modules/ticket/front/index.js b/modules/ticket/front/index.js index 6106a22eb..029dc16a4 100644 --- a/modules/ticket/front/index.js +++ b/modules/ticket/front/index.js @@ -20,7 +20,6 @@ import './package/index'; import './sale'; import './tracking/index'; import './tracking/edit'; -import './sale-checked'; import './services'; import './component'; import './sale-tracking'; @@ -32,7 +31,8 @@ import './weekly'; import './dms/index'; import './dms/create'; import './dms/edit'; -import './sms'; import './boxing'; import './future'; import './future-search-panel'; +import './advance'; +import './advance-search-panel'; diff --git a/modules/ticket/front/routes.json b/modules/ticket/front/routes.json index 2963d54c4..c86b3a1ef 100644 --- a/modules/ticket/front/routes.json +++ b/modules/ticket/front/routes.json @@ -8,7 +8,8 @@ "main": [ {"state": "ticket.index", "icon": "icon-ticket"}, {"state": "ticket.weekly.index", "icon": "schedule"}, - {"state": "ticket.future", "icon": "keyboard_double_arrow_right"} + {"state": "ticket.future", "icon": "keyboard_double_arrow_right"}, + {"state": "ticket.advance", "icon": "keyboard_double_arrow_left"} ], "card": [ {"state": "ticket.card.basicData.stepOne", "icon": "settings"}, @@ -22,7 +23,6 @@ {"state": "ticket.card.expedition", "icon": "icon-package"}, {"state": "ticket.card.service", "icon": "icon-services"}, {"state": "ticket.card.package", "icon": "icon-bucket"}, - {"state": "ticket.card.saleChecked", "icon": "assignment"}, {"state": "ticket.card.components", "icon": "icon-components"}, {"state": "ticket.card.saleTracking", "icon": "assignment"}, {"state": "ticket.card.dms.index", "icon": "cloud_download"}, @@ -158,15 +158,6 @@ }, "acl": ["production", "administrative", "salesPerson"] }, - { - "url" : "/sale-checked", - "state": "ticket.card.saleChecked", - "component": "vn-ticket-sale-checked", - "description": "Sale checked", - "params": { - "ticket": "$ctrl.ticket" - } - }, { "url" : "/components", "state": "ticket.card.components", @@ -290,6 +281,12 @@ "state": "ticket.future", "component": "vn-ticket-future", "description": "Future tickets" + }, + { + "url": "/advance", + "state": "ticket.advance", + "component": "vn-ticket-advance", + "description": "Advance tickets" } ] } diff --git a/modules/ticket/front/sale-checked/index.html b/modules/ticket/front/sale-checked/index.html deleted file mode 100644 index 1bc6f1f68..000000000 --- a/modules/ticket/front/sale-checked/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - Is checked - Item - Description - Quantity - - - - - - - - - - - {{::sale.itemFk | zeroFill:6}} - - - -
- {{::sale.item.name}} - -

{{::sale.item.subName}}

-
-
- - -
- {{::sale.quantity}} -
-
-
-
-
- - diff --git a/modules/ticket/front/sale-checked/index.js b/modules/ticket/front/sale-checked/index.js deleted file mode 100644 index 857ac49e3..000000000 --- a/modules/ticket/front/sale-checked/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import ngModule from '../module'; -import Section from 'salix/components/section'; - -class Controller extends Section { - constructor($element, $) { - super($element, $); - this.filter = { - include: [ - { - relation: 'item' - }, { - relation: 'isChecked', - scope: { - fields: ['isChecked'] - } - } - ] - }; - } - showItemDescriptor(event, sale) { - this.quicklinks = { - btnThree: { - icon: 'icon-transaction', - state: `item.card.diary({ - id: ${sale.itemFk}, - warehouseFk: ${this.ticket.warehouseFk}, - lineFk: ${sale.id} - })`, - tooltip: 'Item diary' - } - }; - this.$.itemDescriptor.show(event.target, sale.itemFk); - } -} - -ngModule.vnComponent('vnTicketSaleChecked', { - template: require('./index.html'), - controller: Controller, - bindings: { - ticket: '<' - } -}); diff --git a/modules/ticket/front/sms/index.html b/modules/ticket/front/sms/index.html deleted file mode 100644 index 97bdfef14..000000000 --- a/modules/ticket/front/sms/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - -
- - - - - - - - - - - {{'Characters remaining' | translate}}: - - {{$ctrl.charactersRemaining()}} - - - -
-
- - - - -
\ No newline at end of file diff --git a/modules/ticket/front/sms/index.spec.js b/modules/ticket/front/sms/index.spec.js deleted file mode 100644 index b133db04d..000000000 --- a/modules/ticket/front/sms/index.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -import './index'; - -describe('Ticket', () => { - describe('Component vnTicketSms', () => { - let controller; - let $httpBackend; - - beforeEach(ngModule('ticket')); - - beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { - $httpBackend = _$httpBackend_; - let $scope = $rootScope.$new(); - const $element = angular.element(''); - controller = $componentController('vnTicketSms', {$element, $scope}); - controller.$.message = { - input: { - value: 'My SMS' - } - }; - })); - - describe('onResponse()', () => { - it('should perform a POST query and show a success snackbar', () => { - let params = {ticketId: 11, destinationFk: 1101, destination: 111111111, message: 'My SMS'}; - controller.sms = {ticketId: 11, destinationFk: 1101, destination: 111111111, message: 'My SMS'}; - - jest.spyOn(controller.vnApp, 'showMessage'); - $httpBackend.expect('POST', `Tickets/11/sendSms`, params).respond(200, params); - - controller.onResponse(); - $httpBackend.flush(); - - expect(controller.vnApp.showMessage).toHaveBeenCalledWith('SMS sent!'); - }); - - it('should call onResponse without the destination and show an error snackbar', () => { - controller.sms = {destinationFk: 1101, message: 'My SMS'}; - - jest.spyOn(controller.vnApp, 'showError'); - - controller.onResponse(); - - expect(controller.vnApp.showError).toHaveBeenCalledWith(`The destination can't be empty`); - }); - - it('should call onResponse without the message and show an error snackbar', () => { - controller.sms = {destinationFk: 1101, destination: 222222222}; - - jest.spyOn(controller.vnApp, 'showError'); - - controller.onResponse(); - - expect(controller.vnApp.showError).toHaveBeenCalledWith(`The message can't be empty`); - }); - }); - - describe('charactersRemaining()', () => { - it('should return the characters remaining in a element', () => { - controller.$.message = { - input: { - value: 'My message 0€' - } - }; - - let result = controller.charactersRemaining(); - - expect(result).toEqual(145); - }); - }); - }); -}); diff --git a/modules/ticket/front/sms/locale/es.yml b/modules/ticket/front/sms/locale/es.yml deleted file mode 100644 index 64c3fcca6..000000000 --- a/modules/ticket/front/sms/locale/es.yml +++ /dev/null @@ -1,9 +0,0 @@ -Send SMS: Enviar SMS -Destination: Destinatario -Message: Mensaje -SMS sent!: ¡SMS enviado! -Characters remaining: Carácteres restantes -The destination can't be empty: El destinatario no puede estar vacio -The message can't be empty: El mensaje no puede estar vacio -The message it's too long: El mensaje es demasiado largo -Special characters like accents counts as a multiple: Carácteres especiales como los acentos cuentan como varios \ No newline at end of file diff --git a/modules/ticket/front/sms/style.scss b/modules/ticket/front/sms/style.scss deleted file mode 100644 index 84571a5f4..000000000 --- a/modules/ticket/front/sms/style.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import "variables"; - -.SMSDialog { - min-width: 400px -} \ No newline at end of file diff --git a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js index 24bfd6904..5b2436be9 100644 --- a/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js +++ b/modules/worker/back/methods/worker-time-control/specs/sendMail.spec.js @@ -10,6 +10,7 @@ describe('workerTimeControl sendMail()', () => { const ctx = {req: activeCtx, args: {}}; it('should fill time control of a worker without records in Journey and with rest', async() => { + pending('https://redmine.verdnatura.es/issues/4903'); const tx = await models.WorkerTimeControl.beginTransaction({}); try { @@ -34,6 +35,7 @@ describe('workerTimeControl sendMail()', () => { }); it('should fill time control of a worker without records in Journey and without rest', async() => { + pending('https://redmine.verdnatura.es/issues/4903'); const workdayOf20Hours = 3; const tx = await models.WorkerTimeControl.beginTransaction({}); @@ -61,6 +63,7 @@ describe('workerTimeControl sendMail()', () => { }); it('should fill time control of a worker with records in Journey and with rest', async() => { + pending('https://redmine.verdnatura.es/issues/4903'); const tx = await models.WorkerTimeControl.beginTransaction({}); try { @@ -92,6 +95,7 @@ describe('workerTimeControl sendMail()', () => { }); it('should fill time control of a worker with records in Journey and without rest', async() => { + pending('https://redmine.verdnatura.es/issues/4903'); const tx = await models.WorkerTimeControl.beginTransaction({}); try { diff --git a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js index 7f652519b..e9924c67b 100644 --- a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js +++ b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js @@ -201,6 +201,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { describe('WorkerTimeControl_clockIn calls', () => { it('should fail to add a time entry if the target user has an absence that day', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; const date = new Date(); @@ -247,6 +248,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { describe('direction errors', () => { it('should throw an error when trying "in" direction twice', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -276,6 +278,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -309,6 +312,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('Should throw an error when trying "out" before closing a "middle" couple', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -342,6 +346,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('should throw an error when trying "middle" after "out"', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -375,6 +380,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('should throw an error when trying "out" direction twice', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -446,6 +452,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('should not fail as the 12h rest is fulfilled', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -482,6 +489,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { describe('for 3500kg drivers with enforced 9h rest', () => { it('should throw an error when the 9h enforced rest is not fulfilled', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = jessicaJonesId; @@ -516,6 +524,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('should not fail when the 9h enforced rest is fulfilled', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = jessicaJonesId; @@ -552,6 +561,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { describe('for 36h weekly rest', () => { it('should throw an error when the 36h weekly rest is not fulfilled', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -586,6 +596,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { }); it('should throw an error when the 36h weekly rest is not fulfilled again', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; @@ -619,6 +630,7 @@ describe('workerTimeControl add/delete timeEntry()', () => { describe('for 72h weekly rest', () => { it('should throw when the 72h weekly rest is not fulfilled yet', async() => { + pending('https://redmine.verdnatura.es/issues/4707'); activeCtx.accessToken.userId = salesBossId; const workerId = hankPymId; diff --git a/print/templates/email/book-entries-imported-incorrectly/assets/css/import.js b/print/templates/email/book-entries-imported-incorrectly/assets/css/import.js new file mode 100644 index 000000000..1582b82c5 --- /dev/null +++ b/print/templates/email/book-entries-imported-incorrectly/assets/css/import.js @@ -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(); + diff --git a/print/templates/email/book-entries-imported-incorrectly/book-entries-imported-incorrectly.html b/print/templates/email/book-entries-imported-incorrectly/book-entries-imported-incorrectly.html new file mode 100644 index 000000000..044ae0312 --- /dev/null +++ b/print/templates/email/book-entries-imported-incorrectly/book-entries-imported-incorrectly.html @@ -0,0 +1,8 @@ + +
+
+

{{ $t('title') }}

+

+
+
+
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/email/supplier-pay-method-update/assets/css/import.js b/print/templates/email/supplier-pay-method-update/assets/css/import.js new file mode 100644 index 000000000..4b4bb7086 --- /dev/null +++ b/print/templates/email/supplier-pay-method-update/assets/css/import.js @@ -0,0 +1,11 @@ +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(); diff --git a/print/templates/email/supplier-pay-method-update/locale/en.yml b/print/templates/email/supplier-pay-method-update/locale/en.yml new file mode 100644 index 000000000..f04ccc5ce --- /dev/null +++ b/print/templates/email/supplier-pay-method-update/locale/en.yml @@ -0,0 +1,3 @@ +subject: Pay method updated +title: Pay method updated +description: The pay method of the supplier {0} has been updated from {1} to {2} diff --git a/print/templates/email/supplier-pay-method-update/locale/es.yml b/print/templates/email/supplier-pay-method-update/locale/es.yml new file mode 100644 index 000000000..59ee0e02f --- /dev/null +++ b/print/templates/email/supplier-pay-method-update/locale/es.yml @@ -0,0 +1,3 @@ +subject: Método de pago actualizado +title: Método de pago actualizado +description: Se ha actualizado el método de pago del proveedor {0} de {1} a {2} diff --git a/print/templates/email/supplier-pay-method-update/supplier-pay-method-update.html b/print/templates/email/supplier-pay-method-update/supplier-pay-method-update.html new file mode 100644 index 000000000..df8543cd9 --- /dev/null +++ b/print/templates/email/supplier-pay-method-update/supplier-pay-method-update.html @@ -0,0 +1,8 @@ + +
+
+

{{ $t('title') }}

+

+
+
+
diff --git a/print/templates/email/supplier-pay-method-update/supplier-pay-method-update.js b/print/templates/email/supplier-pay-method-update/supplier-pay-method-update.js new file mode 100755 index 000000000..283b2689c --- /dev/null +++ b/print/templates/email/supplier-pay-method-update/supplier-pay-method-update.js @@ -0,0 +1,23 @@ +const Component = require(`vn-print/core/component`); +const emailBody = new Component('email-body'); + +module.exports = { + name: 'supplier-pay-method-update', + components: { + 'email-body': emailBody.build(), + }, + props: { + name: { + type: String, + required: true + }, + oldPayMethod: { + type: String, + required: true + }, + newPayMethod: { + type: String, + required: true + } + } +}; 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 @@ - - 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" },
-
Pallet: {{id}}
-
User: {{username.name || '---'}}
-
Day: {{labelData.dayName.toUpperCase() || '---'}}
+
+ +