Merge pull request '8524-devToTest' (!3415) from 8524-devToTest into test
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #3415
Reviewed-by: Guillermo Bonet <guillermo@verdnatura.es>
This commit is contained in:
Alex Moreno 2025-02-04 13:42:14 +00:00
commit 4b903c5bad
127 changed files with 2327 additions and 1758 deletions

View File

@ -52,7 +52,7 @@
},
"payMethod": {
"type": "belongsTo",
"model": "PayMethodFk",
"model": "PayMethod",
"foreignKey": "payMethodFk"
},
"company": {
@ -61,4 +61,4 @@
"foreignKey": "companyFk"
}
}
}
}

View File

@ -321,6 +321,11 @@ UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com'
UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23;
INSERT INTO `vn`.`agencyIncoming`(`agencyModeFk`)
VALUES
(1),
(2);
INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt`, `isIbanRequiredForClients`, `isIbanRequiredForSuppliers`, `hasVerified`)
VALUES
(1, NULL, 'PayMethod one', 0, 001, 0, 0, 0),
@ -745,15 +750,15 @@ INSERT INTO `vn`.`zoneClosure` (`zoneFk`, `dated`, `hour`)
INSERT INTO `vn`.`zoneConfig` (`id`, `scope`) VALUES (1, '1');
INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `zoneFk`, `dated`)
INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `dated`)
VALUES
(1, '1899-12-30 12:15:00', 56, util.VN_CURDATE(), 1, 1, 'first route', 1.8, 10, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1, util.VN_CURDATE()),
(2, '1899-12-30 13:20:00', 56, util.VN_CURDATE(), 1, 2, 'second route', 0.2, 20, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 9, util.VN_CURDATE()),
(3, '1899-12-30 14:30:00', 56, util.VN_CURDATE(), 2, 3, 'third route', 0.5, 30, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 10, util.VN_CURDATE()),
(4, '1899-12-30 15:45:00', 56, util.VN_CURDATE(), 3, 4, 'fourth route', 0, 40, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 12, util.VN_CURDATE()),
(5, '1899-12-30 16:00:00', 56, util.VN_CURDATE(), 4, 5, 'fifth route', 0.1, 50, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 13, util.VN_CURDATE()),
(6, NULL, 57, util.VN_CURDATE(), 5, 7, 'sixth route', 1.7, 60, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 3, util.VN_CURDATE()),
(7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5, util.VN_CURDATE());
(1, '1899-12-30 12:15:00', 133, util.VN_CURDATE(), 1, 1, 'first route', 1.8, 10, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE()),
(2, '1899-12-30 13:20:00', 56, util.VN_CURDATE(), 1, 2, 'second route', 0.2, 20, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE()),
(3, '1899-12-30 14:30:00', 133, util.VN_CURDATE(), 2, 3, 'third route', 0.5, 30, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE()),
(4, '1899-12-30 15:45:00', 56, util.VN_CURDATE(), 3, 4, 'fourth route', 0, 40, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE()),
(5, '1899-12-30 16:00:00', 133, util.VN_CURDATE(), 4, 5, 'fifth route', 0.1, 50, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE()),
(6, NULL, 57, util.VN_CURDATE(), 5, 7, 'sixth route', 1.7, 60, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE()),
(7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), util.VN_CURDATE());
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`, `cmrFk`, `problem`, `risk`)
VALUES
@ -2634,9 +2639,9 @@ REPLACE INTO `vn`.`invoiceIn`(`id`, `serialNumber`,`serial`, `supplierFk`, `issu
(9, 1009, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1242, 0, 442, 1,util.VN_CURDATE()),
(10, 1010, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1243, 0, 442, 1,util.VN_CURDATE());
INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageFarmerWithholdingFk`, `daysAgo`)
INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageFarmerWithholdingFk`, `daysAgo`, `balanceStartingDate`)
VALUES
(1, -2, '2% retention', 2, 45);
(1, -2, '2% retention', 2, 45, '2000-01-01');
INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`)
VALUES
@ -2749,13 +2754,13 @@ INSERT INTO `vn`.`roadmapAddress` (`addressFk`)
(3),
(4);
INSERT INTO `vn`.`roadmap` (`id`, `name`, `tractorPlate`, `trailerPlate`, `phone`, `supplierFk`, `etd`, `observations`, `userFk`, `price`, `driverName`)
INSERT INTO `vn`.`roadmap` (`id`, `name`, `tractorPlate`, `trailerPlate`, `phone`, `supplierFk`, `etd`, `eta`, `observations`, `editorFk`, `price`, `driverName`)
VALUES
(1, 'val-algemesi', '1234-BCD', '9876-BCD', '111111111', 1, util.VN_NOW(), 'this is test observation', 1, 15, 'Batman'),
(2, 'alg-valencia', '2345-CDF', '8765-BCD', '111111111', 1, util.VN_NOW(), 'test observation', 1, 20, 'Robin'),
(3, 'alz-algemesi', '3456-DFG', '7654-BCD', '222222222', 2, DATE_ADD(util.VN_NOW(), INTERVAL 2 DAY), 'observations...', 2, 25, 'Driverman');
(1, 'val-algemesi', '1234-BCD', '9876-BCD', '111111111', 1, util.VN_NOW(), DATE_ADD(util.VN_NOW(), INTERVAL 2 DAY), 'this is test observation', 1, 15, 'Batman'),
(2, 'alg-valencia', '2345-CDF', '8765-BCD', '111111111', 1, util.VN_NOW(), DATE_ADD(util.VN_NOW(), INTERVAL 5 DAY), 'test observation', 1, 20, 'Robin'),
(3, 'alz-algemesi', '3456-DFG', '7654-BCD', '222222222', 2, DATE_ADD(util.VN_NOW(), INTERVAL 3 DAY), DATE_ADD(util.VN_NOW(), INTERVAL 6 DAY), 'observations...', 2, 25, 'Driverman');
INSERT INTO `vn`.`roadmapStop` (`id`, `roadmapFk`, `addressFk`, `eta`, `description`, `userFk`)
INSERT INTO `vn`.`roadmapStop` (`id`, `roadmapFk`, `roadmapAddressFk`, `eta`, `description`, `editorFk`)
VALUES
(1, 1, 1, DATE_ADD(util.VN_NOW(), INTERVAL 1 DAY), 'Best truck in fleet', 1),
(2, 1, 2, DATE_ADD(util.VN_NOW(), INTERVAL '1 2' DAY_HOUR), 'Second truck in fleet', 1),
@ -2919,7 +2924,8 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
(7, 'zone-included','An email to notify zoneCollisions'),
(8, 'backup-printer-selected','A backup printer has been selected'),
(9, 'mrw-deadline','The MRW deadline has passed'),
(10,'invoice-ticket-closure','Tickets not invoiced during the nightly closure ticket process');
(10,'invoice-ticket-closure','Tickets not invoiced during the nightly closure ticket process'),
(11,'misallocation-warehouse','Misallocation of items in the warehouse.');
TRUNCATE `util`.`notificationAcl`;
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
@ -2933,7 +2939,8 @@ INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
(6, 9),
(7, 9),
(8, 66),
(9, 56);
(9, 56),
(11, 9);
TRUNCATE `util`.`notificationQueue`;
INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`)
@ -3200,7 +3207,7 @@ UPDATE vn.department
SET workerFk = null;
INSERT INTO vn.packaging
VALUES('--', 2745600.00, 100.00, 120.00, 220.00, 0.00, 1, '2001-01-01 00:00:00.000', NULL, NULL, NULL, 0.00, 16, 0.00, 0, NULL, 0.00, NULL, NULL, 0, NULL, 0, 0,0,1);
VALUES('--', 2745600.00, 100.00, 120.00, 220.00, 0.00, 1, '2001-01-01 00:00:00.000', NULL, NULL, NULL, 0.00, 16, 0.00, 0, NULL, 0.00, NULL, NULL, 0, NULL, 0, 0,0,1,0);
INSERT IGNORE INTO vn.intrastat
@ -4045,6 +4052,9 @@ INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel)
INSERT INTO vn.workerIrpf (workerFk,spouseNif, geographicMobilityDate)
VALUES (1106,'26493101E','2019-09-20');
INSERT INTO vn.payment (received, supplierFk, amount, currencyFk, divisa, bankFk, payMethodFk, bankingFees, concept, companyFk, created, isConciliated, dueDated, workerFk) VALUES
(util.VN_CURDATE(), 1, 1000.00, 1, NULL, 1, 1, 0.0, 'n/pago', 442, util.VN_CURDATE(), 1, util.VN_CURDATE(), 9);
INSERT INTO vn.referenceRate (currencyFk, dated, value)
VALUES (2, '2000-12-01', 1.0495),
(2, '2001-01-01', 1.0531),
@ -4056,3 +4066,8 @@ INSERT IGNORE INTO vn.osrmConfig (id,url,tolerance)
INSERT IGNORE INTO vn.inventoryConfig
SET id = 1,
supplierFk = 4;
UPDATE vn.worker
SET isFreelance=1
WHERE firstName='deliveryFreelancer';

View File

@ -9,7 +9,7 @@ BEGIN
DECLARE vCalc INT;
DECLARE vWarehouseFk INT;
DECLARE cWarehouses CURSOR FOR
DECLARE cWarehouses CURSOR FOR
SELECT id
FROM vn.warehouse
WHERE isInventory;
@ -22,13 +22,13 @@ BEGIN
read_loop: LOOP
SET vDone = FALSE;
FETCH cWarehouses INTO vWarehouseFk;
IF vDone THEN
LEAVE read_loop;
END IF;
CALL cache.visible_refresh(vCalc, FALSE, vWarehouseFk);
CREATE OR REPLACE TEMPORARY TABLE tVisible
SELECT itemFk, SUM(visible) totalVisible
FROM vn.itemShelving ish
@ -37,7 +37,7 @@ BEGIN
JOIN vn.sector sc ON sc.id = p.sectorFk
WHERE sc.warehouseFk = vWarehouseFk
GROUP BY itemFk;
INSERT INTO inventoryDiscrepancyDetail(
warehouseFk,
itemFk,
@ -65,7 +65,7 @@ BEGIN
JOIN vn.ticketState ts ON ts.ticketFk = t.id
JOIN vn.alertLevel al ON al.id = ts.alertLevel
WHERE t.shipped BETWEEN util.VN_CURDATE() AND util.dayend(util.VN_CURDATE())
AND s.isPicked = FALSE
AND NOT s.isPicked
AND al.code = 'FREE'
AND t.warehouseFk = vWarehouseFk
GROUP BY s.itemFk
@ -73,7 +73,6 @@ BEGIN
) s ON s.itemFk = v.item_id
WHERE v.calc_id = vCalc
AND NOT v.visible <=> tv.totalVisible;
END LOOP;
CLOSE cWarehouses;

View File

@ -15,7 +15,7 @@ BEGIN
DELETE FROM bs.ventas_contables
WHERE year = vYear
AND month = vMonth;
AND month = vMonth;
DROP TEMPORARY TABLE IF EXISTS tmp.ticket_list;
CREATE TEMPORARY TABLE tmp.ticket_list

View File

@ -3,7 +3,7 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `cache`.`available_refres
OUT `vCalc` INT,
`vRefresh` INT,
`vWarehouse` INT,
`vDated` DATE
`vAvailabled` DATETIME
)
proc: BEGIN
DECLARE vStartDate DATE;
@ -12,6 +12,7 @@ proc: BEGIN
DECLARE vInventoryDate DATE;
DECLARE vLifeScope DATE;
DECLARE vWarehouseFkInventory INT;
DECLARE vDated DATE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
@ -19,13 +20,17 @@ proc: BEGIN
RESIGNAL;
END;
IF vDated < util.VN_CURDATE() THEN
IF vAvailabled < util.VN_CURDATE() THEN
LEAVE proc;
END IF;
SET vDated = DATE(vAvailabled);
SET vAvailabled = vDated + INTERVAL HOUR(vAvailabled) HOUR;
CALL vn.item_getStock(vWarehouse, vDated, NULL);
SET vParams = CONCAT_WS('/', vWarehouse, vDated);
SET vParams = CONCAT_WS('/', vWarehouse, vAvailabled);
CALL cache_calc_start (vCalc, vRefresh, 'available', vParams);
IF !vRefresh THEN
@ -87,11 +92,10 @@ proc: BEGIN
SELECT i.itemFk, i.landed, i.quantity
FROM vn.itemEntryIn i
JOIN itemRange ir ON ir.itemFk = i.itemFk
LEFT JOIN edi.warehouseFloramondo wf ON wf.entryFk = i.entryFk
WHERE i.landed >= vStartDate
AND IFNULL(i.availabled, i.landed) <= vAvailabled
AND (ir.ended IS NULL OR i.landed <= ir.ended)
AND i.warehouseInFk = vWarehouse
AND ISNULL(wf.entryFk)
UNION ALL
SELECT i.itemFk, i.shipped, i.quantity
FROM vn.itemEntryOut i

View File

@ -1,11 +1,11 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `sage`.`accountingMovements_add`(
vYear INT,
vYear INT,
vCompanyFk INT
)
BEGIN
/**
* Traslada la info de contabilidad generada en base a vn.XDiario a la tabla sage.movConta
* 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
@ -23,6 +23,7 @@ BEGIN
DECLARE vInvoiceTypeInformativeCode VARCHAR(1);
DECLARE vCountryCanariasCode, vCountryCeutaMelillaCode VARCHAR(2);
DECLARE vCompanyCode INT;
DECLARE vHasErrorTax BOOL DEFAULT FALSE;
SELECT SiglaNacion INTO vCountryCanariasCode
FROM Naciones
@ -44,12 +45,12 @@ BEGIN
FROM taxType
WHERE code = 'import4';
SELECT shipmentTransactionTypeFk,
definitiveExportTransactionTypeFk,
SELECT shipmentTransactionTypeFk,
definitiveExportTransactionTypeFk,
pendingServiceTransactionTypeFk,
company_getCode(vCompanyFk)
INTO vTransactionExportTaxFreeFk,
vTransactionExportFk,
vTransactionExportFk,
vDuaTransactionFk,
vCompanyCode
FROM config;
@ -66,6 +67,24 @@ BEGIN
WHERE enlazadoSage = FALSE
AND Asiento <> 1 ;
SELECT EXISTS (
SELECT TRUE
FROM vn.XDiario x
JOIN vn.invoiceIn ii ON ii.id = x.CLAVE
JOIN vn.invoiceInTax it ON it.invoiceInFk = ii.id
LEFT JOIN TiposIva ti ON ti.CodigoIva = it.taxTypeSageFk
LEFT JOIN taxType tt ON tt.id = it.taxTypeSageFk
WHERE x.FECHA BETWEEN vDatedFrom AND vDatedTo
AND NOT x.enlazadoSage
AND x.empresa_id = vCompanyFk
AND it.taxTypeSageFk
AND (ti.CodigoIva IS NULL OR tt.id IS NULL)
) INTO vHasErrorTax;
IF vHasErrorTax tHEN
CALL util.throw ('Error in tables for received invoices tax');
END IF;
CALL invoiceOut_manager(vYear, vCompanyFk);
CALL invoiceIn_manager(vYear, vCompanyFk);
@ -306,8 +325,8 @@ BEGIN
mci.FechaFacturaOriginal = x.FECHA_EX,
mci.SuFacturaNo = x.FACTURAEX,
mci.FechaOperacion = x.FECHA_OP,
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
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
@ -326,7 +345,7 @@ BEGIN
mci.CodigoIva2 = vTaxImportFk,
mci.IvaDeducible2 = TRUE,
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
x.BASEEURO +
CAST((x.IVA / 100) * x.BASEEURO AS DECIMAL(10, 2))
WHERE pm.description = 'HP Iva pendiente'
AND mci.enlazadoSage = FALSE
@ -344,8 +363,8 @@ BEGIN
mci.CodigoTransaccion3 = vDuaTransactionFk ,
mci.CodigoIva3 = vTaxImportSuperReducedFk,
mci.IvaDeducible3 = TRUE,
mci.ImporteFactura = mci.ImporteFactura +
x.BASEEURO +
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
@ -379,14 +398,14 @@ BEGIN
OR CodigoTransaccion2 = vTransactionExportFk
OR CodigoTransaccion3 = vTransactionExportFk
OR CodigoTransaccion4 = vTransactionExportFk)
AND SiglaNacion IN (vCountryCanariasCode COLLATE utf8mb3_unicode_ci,
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 = 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);
@ -403,20 +422,20 @@ BEGIN
WITH client AS(
SELECT DISTINCT c.id
FROM sage.movConta mc
JOIN vn.client c ON c.accountingAccount = mc.CodigoCuenta
WHERE NOT enlazadoSage
),supplier AS(
JOIN vn.client c ON c.accountingAccount = mc.CodigoCuenta
WHERE NOT enlazadoSage
),supplier AS(
SELECT DISTINCT s.id
FROM sage.movConta mc
JOIN vn.supplier s ON s.account = mc.CodigoCuenta
WHERE NOT enlazadoSage
JOIN vn.supplier s ON s.account = mc.CodigoCuenta
WHERE NOT enlazadoSage
),clientSupplierSync AS(
SELECT idClientSupplier, `type`
FROM sage.clientSupplier cs
WHERE isSync
FROM sage.clientSupplier cs
WHERE isSync
)
SELECT idClientSupplier, `type`
FROM sage.clientSupplier cs
FROM sage.clientSupplier cs
WHERE NOT isSync
UNION
SELECT id, 'C'
@ -424,7 +443,7 @@ BEGIN
LEFT JOIN clientSupplierSync cs ON cs.idClientSupplier = c.id
AND cs.Type ='C'
WHERE cs.idClientSupplier IS NULL
UNION
UNION
SELECT id, 'P'
FROM supplier s
LEFT JOIN clientSupplierSync cs ON cs.idClientSupplier = s.id
@ -436,7 +455,7 @@ BEGIN
INSERT IGNORE INTO sage.clientSupplier (companyFk, `type`, idClientSupplier, isSync)
SELECT vCompanyCode, `type`, idClientSupplier, FALSE
FROM tmp.clientSupplier;
DROP TEMPORARY TABLE tmp.clientSupplier;
CALL pgc_add(vCompanyFk);

View File

@ -0,0 +1,62 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` FUNCTION `vn`.`getTimeBetweenRoadmapAddresses`(
vRoadmapAddressFrom INT,
vRoadmapAddressTo INT
)
RETURNS int(11)
DETERMINISTIC
BEGIN
/**
* Retorna el tiempo en segundos que se suele tardar en ir
* de un punto de distribución a otro en una ruta troncal.
*
* @param vRoadmapAddressFrom Punto de distribución de origen
* @param vRoadmapAddressTo Punto de distribución de destino
* @return Tiempo en segundos
*/
DECLARE vSeconds INT;
WITH wRoadmapStop AS (
SELECT ROW_NUMBER() OVER(PARTITION BY roadmapFk ORDER BY eta) `sequence`,
roadmapFk,
roadmapAddressFk,
eta
FROM vn.roadmapStop
WHERE roadmapFk IS NOT NULL
AND roadmapAddressFk IS NOT NULL
AND eta IS NOT NULL
)
SELECT AVG(TIME_TO_SEC(TIMEDIFF(rsTo.eta, rsFrom.eta))) INTO vSeconds
FROM wRoadmapStop rsFrom
JOIN wRoadmapStop rsTo ON rsTo.roadmapFk = rsFrom.roadmapFk
WHERE rsFrom.roadmapAddressFk = vRoadmapAddressFrom
AND rsTo.roadmapAddressFk = vRoadmapAddressTo
AND rsFrom.`sequence` + 1 = rsTo.`sequence`;
IF NOT IFNULL(vSeconds, 0) THEN
WITH wRoadmap AS (
SELECT id,
roadmapAddressFk,
etd
FROM vn.roadmap
WHERE roadmapAddressFk = vRoadmapAddressFrom
AND etd IS NOT NULL
), wRoadmapStop AS (
SELECT ROW_NUMBER() OVER(PARTITION BY roadmapFk ORDER BY eta) `sequence`,
roadmapFk,
roadmapAddressFk,
eta
FROM vn.roadmapStop
WHERE roadmapFk IS NOT NULL
AND roadmapAddressFk = vRoadmapAddressTo
AND eta IS NOT NULL
)
SELECT AVG(TIME_TO_SEC(TIMEDIFF(rsTo.eta, rFrom.etd))) INTO vSeconds
FROM wRoadmap rFrom
JOIN wRoadmapStop rsTo ON rsTo.roadmapFk = rFrom.id
AND rsTo.`sequence` = 1;
END IF;
RETURN vSeconds;
END$$
DELIMITER ;

View File

@ -1,20 +1,31 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`entry_clone`(vSelf INT)
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`entry_clone`(
vSelf INT,
OUT vOutputEntryFk INT
)
BEGIN
/**
* clones an entry.
*
* @param vSelf The entry id
* @param vOutputEntryFk The new entry id
*/
DECLARE vNewEntryFk INT;
START TRANSACTION;
DECLARE vIsRequiredTx BOOL DEFAULT NOT @@in_transaction;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
CALL util.tx_rollback(vIsRequiredTx);
RESIGNAL;
END;
CALL util.tx_start(vIsRequiredTx);
CALL entry_cloneHeader(vSelf, vNewEntryFk, NULL);
CALL entry_copyBuys(vSelf, vNewEntryFk);
COMMIT;
CALL util.tx_commit(vIsRequiredTx);
SET vOutputEntryFk = vNewEntryFk;
SELECT vNewEntryFk;
END$$
DELIMITER ;

View File

@ -39,14 +39,14 @@ BEGIN
read_loop: LOOP
SET vDone = FALSE;
FETCH cur INTO vBuyFk, vIshStickers, vBuyStickers;
IF vDone THEN
LEAVE read_loop;
END IF;
IF vIshStickers = vBuyStickers THEN
IF vIshStickers = vBuyStickers THEN
UPDATE buy
SET entryFk = vToEntryFk
WHERE id = vBuyFk;

View File

@ -0,0 +1,158 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`entry_transfer`(
vOriginalEntry INT,
OUT vNewEntryFk INT
)
BEGIN
/**
* Adelanta a mañana la mercancia de una entrada a partir de lo que hay ubicado en el almacén
*
* @param vOriginalEntry entrada que se quiera adelantar
* @param vNewEntry nueva entrada creada
*/
DECLARE vTravelFk INT;
DECLARE vWarehouseFk INT;
DECLARE vWarehouseInFk INT;
DECLARE vWarehouseOutFk INT;
DECLARE vRef INT;
DECLARE vIsReceived INT;
DECLARE vAgencyModeFk INT;
DECLARE vTomorrow DATETIME DEFAULT util.tomorrow();
DECLARE vCurDate DATE DEFAULT util.VN_CURDATE();
DECLARE vIsRequiredTx BOOL DEFAULT NOT @@in_transaction;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
CALL util.tx_rollback(vIsRequiredTx);
RESIGNAL;
END;
-- Clonar la entrada
CALL entry_clone(vOriginalEntry, vNewEntryFk);
CALL util.tx_start(vIsRequiredTx);
/* Hay que crear un nuevo travel, con salida hoy y llegada mañana y
asignar la entrada nueva al nuevo travel.*/
SELECT t.warehouseInFk, t.warehouseOutFk, t.`ref`, t.isReceived, t.agencyModeFk
INTO vWarehouseInFk, vWarehouseOutFk, vRef, vIsReceived, vAgencyModeFk
FROM travel t
JOIN entry e ON e.travelFk = t.id
WHERE e.id = vOriginalEntry;
SELECT id INTO vTravelFk
FROM travel t
WHERE shipped = vCurDate
AND landed = vTomorrow
AND warehouseInFk = vWarehouseInFk
AND warehouseOutFk = vWarehouseOutFk
AND `ref` = vRef
AND isReceived =vIsReceived
AND agencyModeFk = vAgencyModeFk;
IF vTravelFk IS NULL THEN
INSERT INTO travel(
shipped,
landed,
warehouseInFk,
warehouseOutFk,
`ref`,
isReceived,
agencyModeFk)
SELECT vCurDate,
vTomorrow,
t.warehouseInFk,
t.warehouseOutFk,
t.`ref`,
t.isReceived,
t.agencyModeFk
FROM travel t
JOIN entry e ON e.travelFk = t.id
WHERE e.id = vOriginalEntry;
SET vTravelFk = LAST_INSERT_ID();
END IF;
UPDATE entry
SET travelFk = vTravelFk,
evaNotes = vOriginalEntry
WHERE id = vNewEntryFk;
-- Poner a 0 las cantidades
UPDATE buy b
SET b.quantity = 0, b.stickers = 0
WHERE b.entryFk = vNewEntryFk;
-- Eliminar duplicados
DELETE b
FROM buy b
LEFT JOIN (SELECT b.id, b.itemFk
FROM buy b
WHERE b.entryFk = vNewEntryFk
GROUP BY b.itemFk) tBuy ON tBuy.id = b.id
WHERE b.entryFk = vNewEntryFk
AND tBuy.id IS NULL;
SELECT t.warehouseInFk INTO vWarehouseFk
FROM travel t
JOIN entry e ON e.travelFk = t.id
WHERE e.id = vOriginalEntry;
/* Actualizar nueva entrada con lo que no está ubicado HOY,
descontando lo vendido HOY de esas ubicaciones*/
CREATE OR REPLACE TEMPORARY TABLE buys
WITH tBuy AS (
SELECT b.itemFk, SUM(b.quantity) totalQuantity
FROM vn.buy b
WHERE b.entryFk = vOriginalEntry
GROUP BY b.itemFk
),
itemShelvings AS (
SELECT ish.itemFk, SUM(ish.visible) visible
FROM vn.itemShelving ish
JOIN vn.shelving sh ON sh.id = ish.shelvingFk
JOIN vn.parking p ON p.id = sh.parkingFk
JOIN vn.sector s ON s.id = p.sectorFk
JOIN vn.buy b ON b.id = ish.buyFk
JOIN vn.entry e ON e.id = b.entryFk
JOIN tBuy t ON t.itemFk = ish.itemFk
WHERE s.warehouseFk = vWarehouseFk
AND sh.parked >= vCurDate
GROUP BY ish.itemFk
),
sales AS (
SELECT s.itemFk, SUM(s.quantity) sold
FROM vn.ticket t
JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.itemShelvingSale iss ON iss.saleFk = s.id
JOIN vn.itemShelving is2 ON is2.id = iss.itemShelvingFk
JOIN vn.shelving s2 ON s2.id = is2.shelvingFk
JOIN tBuy t ON t.itemFk = s.itemFk
WHERE t.shipped BETWEEN vCurDate AND util.dayend(vCurDate)
AND s2.parked >= vCurDate
GROUP BY s.itemFk
)
SELECT tmp.itemFk,
IFNULL(iss.visible, 0) visible,
tmp.totalQuantity,
IFNULL(s.sold, 0) sold
FROM tBuy tmp
LEFT JOIN itemShelvings iss ON tmp.itemFk = iss.itemFk
LEFT JOIN sales s ON s.itemFk = tmp.itemFk
WHERE visible < tmp.totalQuantity
OR iss.itemFk IS NULL;
UPDATE buy b
JOIN buys tmp ON tmp.itemFk = b.itemFk
SET b.quantity = tmp.totalQuantity - tmp.visible - tmp.sold
WHERE b.entryFk = vNewEntryFk;
-- Limpia la nueva entrada
DELETE FROM buy WHERE entryFk = vNewEntryFk AND quantity = 0;
CALL util.tx_commit(vIsRequiredTx);
CALL cache.visible_refresh(@c,TRUE,vWarehouseFk);
CALL cache.available_refresh(@c, TRUE, vWarehouseFk, vCurDate);
END$$
DELIMITER ;

View File

@ -1,207 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`itemShelvingRadar`(
vSectorFk INT
)
BEGIN
/**
* Calcula la información detallada respecto un sector.
*
* @param vSectorFk Id de sector
*/
DECLARE vCalcVisibleFk INT;
DECLARE vCalcAvailableFk INT;
DECLARE hasFatherSector BOOLEAN;
DECLARE vBuyerFk INT DEFAULT 0;
DECLARE vWarehouseFk INT DEFAULT 0;
DECLARE vSonSectorFk INT;
DECLARE vWorkerFk INT;
SELECT s.workerFk INTO vWorkerFk
FROM sector s
WHERE s.id = vSectorFk;
SELECT COUNT(*) INTO hasFatherSector
FROM sector
WHERE sonFk = vSectorFk;
SELECT warehouseFk, sonFk INTO vWarehouseFk, vSonSectorFk
FROM sector
WHERE id = vSectorFk;
CALL cache.visible_refresh(vCalcVisibleFk, TRUE, vWarehouseFk);
CALL cache.available_refresh(vCalcAvailableFk, FALSE, vWarehouseFk, util.VN_CURDATE());
IF hasFatherSector THEN
CREATE OR REPLACE TEMPORARY TABLE tItemShelvingRadar
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT *
FROM (
SELECT iss.itemFk,
i.longName,
i.size,
i.subName producer,
IFNULL(a.available, 0) available,
SUM(IF(s.sonFk = vSectorFk, IFNULL(iss.visible, 0), 0)) upstairs,
SUM(IF(iss.sectorFk = vSectorFk, IFNULL(iss.visible, 0), 0)) downstairs,
IF(it.isPackaging, NULL, IFNULL(v.visible, 0)) visible,
vSectorFk sectorFk,
ish.isChecked,
sub.isAllChecked
FROM itemShelvingStock iss
JOIN itemShelving ish ON ish.id = iss.itemShelvingFk
LEFT JOIN (
SELECT itemFk,
IF(
COUNT(*) = SUM(IF(isChecked >= 0, 1, 0)),
TRUE,
FALSE
) isAllChecked
FROM itemShelving is2
GROUP BY itemFk
) sub ON sub.itemFk = ish.itemFk
JOIN sector s ON s.id = iss.sectorFk
JOIN item i ON i.id = iss.itemFk
JOIN itemType it ON it.id = i.typeFk
LEFT JOIN cache.available a ON a.item_id = iss.itemFk
AND a.calc_id = vCalcAvailableFk
LEFT JOIN cache.visible v ON v.item_id = iss.itemFk
AND v.calc_id = vCalcVisibleFk
WHERE vSectorFk IN (iss.sectorFk, s.sonFk)
GROUP BY iss.itemFk
UNION ALL
SELECT v.item_id,
i.longName,
i.size,
i.subName,
IFNULL(a.available, 0),
0,
0,
IF(it.isPackaging, NULL, v.visible),
vSectorFk,
NULL,
NULL
FROM cache.visible v
JOIN item i ON i.id = v.item_id
JOIN itemType it ON it.id = i.typeFk
LEFT JOIN itemShelvingStock iss ON iss.itemFk = v.item_id
AND iss.warehouseFk = vWarehouseFk
LEFT JOIN cache.available a ON a.item_id = v.item_id
AND a.calc_id = vCalcAvailableFk
WHERE v.calc_id = vCalcVisibleFk
AND iss.itemFk IS NULL
AND it.isInventory
) sub
GROUP BY itemFk;
SELECT ishr.*,
CAST(visible - upstairs - downstairs AS DECIMAL(10, 0)) nicho,
CAST(downstairs - IFNULL(notPickedYed, 0) AS DECIMAL(10, 0)) pendiente
FROM tItemShelvingRadar ishr
JOIN item i ON i.id = ishr.itemFk
LEFT JOIN (
SELECT s.itemFk, SUM(s.quantity) notPickedYed
FROM ticket t
JOIN ticketStateToday tst ON tst.ticketFk = t.id
JOIN alertLevel al ON al.id = tst.alertLevel
JOIN sale s ON s.ticketFk = t.id
WHERE t.warehouseFk = vWarehouseFk
AND al.code = 'FREE'
GROUP BY s.itemFk
) sub ON sub.itemFk = ishr.itemFk
ORDER BY i.typeFk, i.longName;
ELSE
CREATE OR REPLACE TEMPORARY TABLE tItemShelvingRadar
(PRIMARY KEY (itemFk))
ENGINE = MEMORY
SELECT iss.itemFk,
0 `hour`,
0 `minute`,
'--' itemPlacementCode,
i.longName,
i.size,
i.subName producer,
i.upToDown,
IFNULL(a.available, 0) available,
IFNULL(v.visible - iss.visible, 0) dayEndVisible,
IFNULL(v.visible - iss.visible, 0) firstNegative,
IFNULL(v.visible - iss.visible, 0) itemPlacementVisible,
IFNULL(i.minimum * b.packing, 0) itemPlacementSize,
ips.onTheWay,
iss.visible itemShelvingStock,
IFNULL(v.visible, 0) visible,
b.isPickedOff,
iss.sectorFk
FROM itemShelvingStock iss
JOIN item i ON i.id = iss.itemFk
LEFT JOIN cache.last_buy lb ON lb.item_id = iss.itemFk
AND lb.warehouse_id = vWarehouseFk
LEFT JOIN buy b ON b.id = lb.buy_id
LEFT JOIN cache.available a ON a.item_id = iss.itemFk
AND a.calc_id = vCalcAvailableFk
LEFT JOIN cache.visible v ON v.item_id = iss.itemFk
AND v.calc_id = vCalcVisibleFk
LEFT JOIN (
SELECT itemFk, SUM(saldo) onTheWay
FROM itemPlacementSupplyList
WHERE saldo > 0
GROUP BY itemFk
) ips ON ips.itemFk = i.id
WHERE iss.sectorFk = vSectorFk
OR iss.sectorFk IS NULL;
CREATE OR REPLACE TEMPORARY TABLE tmp.itemOutTime
SELECT *, SUM(amount) quantity
FROM (
SELECT io.itemFk,
io.quantity amount,
IF(HOUR(t.shipped), HOUR(t.shipped), HOUR(z.`hour`)) `hours`,
IF(MINUTE(t.shipped), MINUTE(t.shipped), MINUTE(z.`hour`)) `minutes`
FROM itemTicketOut `io`
JOIN tItemShelvingRadar isr ON isr.itemFk = io.itemFk
JOIN ticket t ON t.id= io.ticketFk
JOIN ticketState ts ON ts.ticketFk = io.ticketFk
JOIN `state` s ON s.id = ts.stateFk
LEFT JOIN `zone` z ON z.id = t.zoneFk
LEFT JOIN (
SELECT DISTINCT saleFk
FROM saleTracking st
WHERE st.created > util.VN_CURDATE()
AND st.isChecked
) stPrevious ON stPrevious.saleFk = io.saleFk
WHERE t.warehouseFk = vWarehouseFk
AND NOT s.isPicked
AND NOT io.reserved
AND stPrevious.saleFk IS NULL
AND io.shipped >= util.VN_CURDATE()
AND io.shipped < util.VN_CURDATE() + INTERVAL 1 DAY
) sub
GROUP BY itemFk, `hours`, `minutes`;
INSERT INTO tItemShelvingRadar (itemFk)
SELECT itemFk FROM tmp.itemOutTime
ON DUPLICATE KEY UPDATE dayEndVisible = dayEndVisible + quantity,
firstNegative = IF(firstNegative < 0, firstNegative, firstNegative + quantity),
`hour` = IFNULL(IF(firstNegative > 0 , `hour`, `hours`), 0),
`minute` = IFNULL(IF(firstNegative > 0, `minute`, `minutes`), 0);
UPDATE tItemShelvingRadar isr
JOIN (
SELECT s.itemFk, SUM(s.quantity) amount
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
JOIN ticketState ts ON ts.ticketFk = t.id
WHERE t.shipped BETWEEN util.VN_CURDATE() AND util.dayend(util.VN_CURDATE())
AND ts.code = 'COOLER_PREPARATION'
GROUP BY s.itemFk
) sub ON sub.itemFk = isr.itemFk
SET isr.dayEndVisible = dayEndVisible + sub.amount,
firstNegative = firstNegative + sub.amount;
SELECT * FROM tItemShelvingRadar;
END IF;
DROP TEMPORARY TABLE tItemShelvingRadar;
END$$
DELIMITER ;

View File

@ -15,8 +15,6 @@ BEGIN
*
* @return tmp.itemList(itemFk, stock, visible, available)
*/
DECLARE vIsLogifloraDay BOOL DEFAULT vn.isLogifloraDay(vDated, vWarehouseFk);
SET vDated = TIMESTAMP(vDated, '00:00:00');
CREATE OR REPLACE TEMPORARY TABLE tmp.itemList
@ -36,14 +34,11 @@ BEGIN
UNION ALL
SELECT iei.itemFk, iei.quantity
FROM itemEntryIn iei
LEFT JOIN edi.warehouseFloramondo wf ON wf.entryFk = iei.entryFk
JOIN item i ON i.id = iei.itemFk
WHERE iei.landed >= util.VN_CURDATE()
AND iei.landed < vDated
AND iei.warehouseInFk = vWarehouseFk
AND (vItemFk IS NULL OR iei.itemFk = vItemFk)
AND (wf.entryFk IS NULL OR vIsLogifloraDay)
AND NOT (iei.landed > util.VN_CURDATE() AND i.isFloramondo)
UNION ALL
SELECT ieo.itemFk, ieo.quantity
FROM itemEntryOut ieo
@ -52,7 +47,6 @@ BEGIN
AND ieo.shipped < vDated
AND ieo.warehouseOutFk = vWarehouseFk
AND (vItemFk IS NULL OR ieo.itemFk = vItemFk)
AND NOT (ieo.shipped > util.VN_CURDATE() AND i.isFloramondo)
) sub
GROUP BY itemFk
HAVING stock;

View File

@ -266,14 +266,14 @@ proc: BEGIN
UPDATE tmp.productionBuffer pb
JOIN sale s ON s.ticketFk = pb.ticketFk
JOIN item i ON i.id = s.itemFk
JOIN cache.last_buy lb ON lb.warehouse_id = vWarehouseFk
JOIN cache.last_buy lb ON lb.warehouse_id = vWarehouseFk
AND lb.item_id = s.itemFk
JOIN buy b ON b.id = lb.buy_id
JOIN packaging p ON p.id = b.packagingFk
SET pb.hasPlantTray = TRUE
WHERE p.isPlantTray
AND s.quantity >= b.packing
AND pb.isOwn;
AND pb.isOwn;
DROP TEMPORARY TABLE
tmp.productionTicket,

View File

@ -0,0 +1,72 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`roadmap_cloneDay`(
vDateToCopy DATE,
vDateToPaste DATE
)
BEGIN
/**
* Clona roadmaps de un día a otro, incluyendo las paradas y sin algunos
* campos de la tabla principal, como matrículas, conductores...
*
* @param vDateToCopy Fecha para copiar
* @param vDateToPaste Fecha para pegar
*/
DECLARE vDaysDiff INT;
DECLARE vRoadmapFk INT;
DECLARE vNewRoadmapFk INT;
DECLARE vDone BOOL DEFAULT FALSE;
DECLARE vRoadmaps CURSOR FOR
SELECT id
FROM roadmap
WHERE etd BETWEEN vDateToCopy AND util.dayEnd(vDateToCopy);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
SET vDaysDiff = DATEDIFF(vDateToPaste, vDateToCopy);
IF vDaysDiff IS NULL THEN
CALL util.throw("No valid dates");
END IF;
START TRANSACTION;
OPEN vRoadmaps;
l: LOOP
SET vDone = FALSE;
FETCH vRoadmaps INTO vRoadmapFk;
IF vDone THEN
LEAVE l;
END IF;
INSERT INTO roadmap (`name`, roadmapAddressFk, etd, eta, observations, price)
SELECT `name`,
roadmapAddressFk,
etd + INTERVAL vDaysDiff DAY,
eta + INTERVAL vDaysDiff DAY,
observations,
price
FROM roadmap
WHERE id = vRoadmapFk;
SET vNewRoadmapFk = LAST_INSERT_ID();
INSERT INTO roadmapStop (roadmapFk, roadmapAddressFk, eta, `description`, bufferFk)
SELECT vNewRoadmapFk,
roadmapAddressFk,
eta + INTERVAL vDaysDiff DAY,
`description`,
bufferFk
FROM roadmapStop
WHERE roadmapFk = vRoadmapFk;
END LOOP;
CLOSE vRoadmaps;
COMMIT;
END$$
DELIMITER ;

View File

@ -41,6 +41,7 @@ BEGIN
) currencyBalance
FROM (
SELECT NULL bankFk,
NULL bank,
ii.companyFk,
ii.serial,
ii.id,
@ -74,6 +75,7 @@ BEGIN
GROUP BY iid.id, ii.id
UNION ALL
SELECT p.bankFk,
a.bank,
p.companyFk,
NULL,
p.id,
@ -109,6 +111,7 @@ BEGIN
AND (vIsConciliated = p.isConciliated OR NOT vIsConciliated)
UNION ALL
SELECT NULL,
NULL bankFk,
companyFk,
NULL,
se.id,
@ -136,6 +139,7 @@ BEGIN
AND (vIsConciliated = se.isConciliated OR NOT vIsConciliated)
UNION ALL
SELECT NULL bankFk,
NULL,
e.companyFk,
'E' serial,
e.invoiceNumber id,
@ -154,7 +158,7 @@ BEGIN
JOIN travel tr ON tr.id = e.travelFk
JOIN currency c ON c.id = e.currencyFk
WHERE e.supplierFk = vSupplierFk
AND tr.landed >= CURDATE()
AND tr.landed >= util.VN_CURDATE()
AND e.invoiceInFk IS NULL
AND vHasEntries
ORDER BY (dated IS NULL AND NOT isBooked),

View File

@ -1,36 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`ticket_DelayTruck`(vWarehouserFk INT, vHour INT, vMinute INT)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE vTicketFk INT;
DECLARE cur1 CURSOR FOR SELECT ticketFk FROM tTicket;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
CALL vn.productionControl(vWarehouserFk,0) ;
DROP TEMPORARY TABLE IF EXISTS tTicket;
CREATE TEMPORARY TABLE tTicket
SELECT ticketFk
FROM tmp.productionBuffer
JOIN alertLevel al ON al.code = 'FREE'
WHERE shipped = util.VN_CURDATE()
AND problem LIKE '%I:%'
AND (HH <= vHour OR HH = vHour AND mm < vMinute)
AND alertLevel = al.id;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO vTicketFk;
IF done THEN
LEAVE read_loop;
END IF;
CALL vn.ticket_DelayTruckSplit(vTicketFk);
END LOOP;
CLOSE cur1;
DROP TEMPORARY TABLE tTicket, tmp.productionBuffer;
END$$
DELIMITER ;

View File

@ -1,59 +0,0 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`ticket_DelayTruckSplit`(
vTicketFk INT
)
BEGIN
/**
* Splita las lineas de ticket que no estan ubicadas
*
* @param vTicketFk Id ticket
*/
DECLARE vNewTicketFk INT;
DECLARE vTotalLines INT;
DECLARE vLinesToSplit INT;
DROP TEMPORARY TABLE IF EXISTS tmp.SalesToSplit;
SELECT COUNT(*) INTO vTotalLines
FROM sale
WHERE ticketFk = vTicketFk;
CREATE TEMPORARY TABLE tmp.SalesToSplit
SELECT s.id saleFk
FROM ticket t
JOIN sale s ON t.id = s.ticketFk
LEFT JOIN (
SELECT ish.itemFk itemFk,
SUM(ish.visible) visible,
s.warehouseFk warehouseFk
FROM itemShelving ish
JOIN shelving sh ON sh.id = ish.shelvingFk
JOIN parking p ON p.id = sh.parkingFk
JOIN sector s ON s.id = p.sectorFk
GROUP BY ish.itemFk,
s.warehouseFk
) issw ON issw.itemFk = s.itemFk
AND issw.warehouseFk = t.warehouseFk
WHERE s.quantity > IFNULL(issw.visible, 0)
AND s.quantity > 0
AND NOT s.isPicked
AND NOT s.reserved
AND t.id = vTicketFk;
SELECT COUNT(*) INTO vLinesToSplit
FROM tmp.SalesToSplit;
IF vLinesToSplit = vTotalLines AND vLinesToSplit > 0 THEN
SET vNewTicketFk = vTicketFk;
ELSE
CALL ticket_Clone(vTicketFk, vNewTicketFk);
UPDATE sale s
JOIN tmp.SalesToSplit sts ON sts.saleFk = s.id
SET s.ticketFk = vNewTicketFk;
END IF;
CALL ticket_setState(vNewTicketFk, 'FIXING');
DROP TEMPORARY TABLE tmp.SalesToSplit;
END$$
DELIMITER ;

View File

@ -3,7 +3,7 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`ticket_canMerge`(vDat
BEGIN
/**
* Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro
*
*
* @param vDated Fecha en cuestión
* @param vScopeDays Dias en el futuro a sondear
* @param vLitersMax Volumen máximo de los tickets a catapultar

View File

@ -19,6 +19,7 @@ BEGIN
sub2.iptd futureIpt,
sub2.state futureState,
t.clientFk,
cl.salespersonFk,
t.warehouseFk,
ts.alertLevel,
sub2.alertLevel futureAlertLevel,
@ -38,6 +39,7 @@ BEGIN
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.client cl ON cl.id = t.clientFk
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

View File

@ -1,14 +1,21 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`vehicle_checkNumberPlate`(vNumberPlate VARCHAR(10), vCountryCodeFk VARCHAR(2))
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`vehicle_checkNumberPlate`(
vNumberPlate VARCHAR(10),
vCountryCodeFk VARCHAR(2)
)
BEGIN
/**
* Comprueba si la matricula pasada tiene el formato correcto dependiendo del pais del vehiculo
* Comprueba si la matricula pasada tiene el formato
* correcto dependiendo del pais del vehiculo.
*
* @param vNumberPlate Número de matricula
* @param vCountryCodeFk Código de pais
*/
DECLARE vRegex VARCHAR(45);
SELECT vp.regex INTO vRegex
FROM vehiclePlateRegex vp
WHERE vp.countryCodeFk = vCountryCodeFk;
SELECT regex INTO vRegex
FROM vehiclePlateRegex
WHERE countryCodeFk = vCountryCodeFk;
IF NOT vNumberPlate REGEXP BINARY (vRegex)THEN
CALL util.throw(CONCAT('Error: la matricula ', vNumberPlate, ' no es valida para ',vCountryCodeFk));

View File

@ -0,0 +1,26 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`roadmapStop_beforeDelete`
BEFORE DELETE ON `roadmapStop`
FOR EACH ROW
BEGIN
DECLARE vMaxEta DATETIME;
DECLARE vRoadmapEta DATETIME;
IF OLD.roadmapFk IS NOT NULL THEN
SELECT MAX(eta) INTO vMaxEta
FROM roadmapStop
WHERE roadmapFk = OLD.roadmapFk
AND id <> OLD.id;
SELECT eta INTO vRoadmapEta
FROM roadmap
WHERE id = OLD.roadmapFk;
IF vMaxEta <> vRoadmapEta OR vMaxEta IS NULL THEN
UPDATE roadmap
SET eta = vMaxEta
WHERE id = OLD.roadmapFk;
END IF;
END IF;
END$$
DELIMITER ;

View File

@ -3,8 +3,30 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`roadmapStop_beforeInser
BEFORE INSERT ON `roadmapStop`
FOR EACH ROW
BEGIN
DECLARE vRoadmapEta DATETIME;
SET NEW.description = UCASE(NEW.description);
SET NEW.editorFk = account.myUser_getId();
IF NEW.description IS NOT NULL THEN
SET NEW.description = UCASE(NEW.description);
END IF;
IF NEW.roadmapFk IS NOT NULL THEN
IF NEW.eta < (SELECT etd FROM roadmap WHERE id = NEW.roadmapFk) THEN
CALL util.throw('Departure time can not be after arrival time');
END IF;
END IF;
IF NEW.roadmapFk IS NOT NULL AND NEW.eta IS NOT NULL THEN
SELECT eta INTO vRoadmapEta
FROM roadmap
WHERE id = NEW.roadmapFk;
IF vRoadmapEta < NEW.eta OR vRoadmapEta IS NULL THEN
UPDATE roadmap
SET eta = NEW.eta
WHERE id = NEW.roadmapFk;
END IF;
END IF;
END$$
DELIMITER ;
DELIMITER ;

View File

@ -3,8 +3,40 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`roadmapStop_beforeUpdat
BEFORE UPDATE ON `roadmapStop`
FOR EACH ROW
BEGIN
DECLARE vMaxEta DATETIME;
DECLARE vCurrentEta DATETIME;
SET NEW.description = UCASE(NEW.description);
SET NEW.editorFk = account.myUser_getId();
IF NOT (NEW.description <=> OLD.description) THEN
SET NEW.description = UCASE(NEW.description);
END IF;
IF (NOT (NEW.roadmapFk <=> OLD.roadmapFk) AND NEW.roadmapFk IS NOT NULL)
OR (NOT (NEW.eta <=> OLD.eta)) THEN
IF NEW.eta < (SELECT etd FROM roadmap WHERE id = NEW.roadmapFk) THEN
CALL util.throw('Departure time can not be after arrival time');
END IF;
SELECT MAX(eta) INTO vMaxEta
FROM roadmapStop
WHERE roadmapFk = NEW.roadmapFk
AND id <> OLD.id;
IF vMaxEta < NEW.eta OR vMaxEta IS NULL THEN
SET vMaxEta = NEW.eta;
END IF;
SELECT eta INTO vCurrentEta
FROM roadmap
WHERE id = NEW.roadmapFk;
IF (vMaxEta <> vCurrentEta OR vMaxEta IS NULL) OR vMaxEta IS NOT NULL THEN
UPDATE roadmap
SET eta = vMaxEta
WHERE id = NEW.roadmapFk;
END IF;
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,17 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`roadmap_afterUpdate`
AFTER UPDATE ON `roadmap`
FOR EACH ROW
BEGIN
DECLARE vSeconds INT;
IF NOT (NEW.etd <=> OLD.etd) THEN
SET vSeconds = TIME_TO_SEC(TIMEDIFF(NEW.etd, OLD.etd));
IF vSeconds IS NOT NULL AND vSeconds <> 0 THEN
UPDATE roadmapStop
SET eta = eta + INTERVAL vSeconds SECOND
WHERE roadmapFk = NEW.id;
END IF;
END IF;
END$$
DELIMITER ;

View File

@ -3,10 +3,31 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`roadmap_beforeInsert`
BEFORE INSERT ON `roadmap`
FOR EACH ROW
BEGIN
SET NEW.editorFk = account.myUser_getId();
IF NEW.name IS NOT NULL THEN
SET NEW.name = UCASE(NEW.name);
END IF;
IF NEW.trailerPlate IS NOT NULL OR NEW.tugPlate IS NOT NULL THEN
SET NEW.m3 = (SELECT SUM(m3) FROM vehicle WHERE numberPlate IN (NEW.trailerPlate, NEW.tugPlate));
END IF;
IF NEW.driver1Fk IS NOT NULL THEN
SET NEW.driverName = (SELECT firstName FROM worker WHERE id = NEW.driver1Fk);
ELSE
SET NEW.driverName = NULL;
SET NEW.driverName = (SELECT CONCAT(w.firstName, ' ', w.lastName)
FROM worker w
WHERE w.id = NEW.driver1Fk);
SET NEW.phone = (SELECT COALESCE(w.phone, c.mobile, c.phone, c.mobile)
FROM worker w
LEFT JOIN client c ON c.id = w.id
WHERE w.id = NEW.driver1Fk);
END IF;
IF NEW.driverChangeFk IS NOT NULL THEN
SET NEW.driverChangeName = (SELECT CONCAT(w.firstName, ' ', w.lastName)
FROM worker w
WHERE w.id = NEW.driverChangeFk);
END IF;
END$$
DELIMITER ;

View File

@ -3,10 +3,39 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`roadmap_beforeUpdate`
BEFORE UPDATE ON `roadmap`
FOR EACH ROW
BEGIN
IF NEW.driver1Fk IS NOT NULL THEN
SET NEW.driverName = (SELECT firstName FROM worker WHERE id = NEW.driver1Fk);
ELSE
SET NEW.driverName = NULL;
SET NEW.editorFk = account.myUser_getId();
IF NOT (NEW.name <=> OLD.name) THEN
SET NEW.name = UCASE(NEW.name);
END IF;
IF NOT (NEW.trailerPlate <=> OLD.trailerPlate) OR NOT (NEW.tugPlate <=> OLD.tugPlate) THEN
SET NEW.m3 = (SELECT SUM(m3) FROM vehicle WHERE numberPlate IN (NEW.trailerPlate, NEW.tugPlate));
END IF;
IF NOT (NEW.driverName <=> OLD.driverName) THEN
SET NEW.driver1Fk = NULL;
END IF;
IF NOT (NEW.driver1Fk <=> OLD.driver1Fk) AND NEW.driver1Fk IS NOT NULL THEN
SET NEW.driverName = (SELECT CONCAT(w.firstName, ' ', w.lastName)
FROM worker w
WHERE w.id = NEW.driver1Fk);
SET NEW.phone = (SELECT COALESCE(w.phone, c.mobile, c.phone, c.mobile)
FROM worker w
LEFT JOIN client c ON c.id = w.id
WHERE w.id = NEW.driver1Fk);
END IF;
IF NOT (NEW.driverChangeName <=> OLD.driverChangeName) THEN
SET NEW.driverChangeFk = NULL;
END IF;
IF NOT (NEW.driverChangeFk <=> OLD.driverChangeFk) AND NEW.driverChangeFk IS NOT NULL THEN
SET NEW.driverChangeName = (SELECT CONCAT(w.firstName, ' ', w.lastName)
FROM worker w
WHERE w.id = NEW.driverChangeFk);
END IF;
END$$
DELIMITER ;

View File

@ -16,5 +16,9 @@ BEGIN
IF NEW.awbFk IS NOT NULL THEN
CALL travel_throwAwb(NEW.id);
END IF;
IF NEW.availabled < NEW.landed THEN
CALL util.throw('The travel availabled cannot be earlier than landed');
END IF;
END$$
DELIMITER ;

View File

@ -40,5 +40,9 @@ BEGIN
IF (NOT(NEW.awbFk <=> OLD.awbFk)) AND NEW.awbFk IS NOT NULL THEN
CALL travel_throwAwb(NEW.id);
END IF;
IF NEW.availabled < NEW.landed THEN
CALL util.throw('The travel availabled cannot be earlier than landed');
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,9 @@
CREATE OR REPLACE DEFINER=`vn`@`localhost`
SQL SECURITY DEFINER
VIEW `vn`.`agencyModeIncoming` AS
SELECT
am.id,
am.name
FROM `vn`.`agencyMode` AS am
JOIN `vn`.`agencyIncoming` AS ai
ON am.id = ai.agencyModeFk;

View File

@ -7,7 +7,8 @@ AS SELECT `t`.`warehouseInFk` AS `warehouseInFk`,
`b`.`quantity` AS `quantity`,
`t`.`isReceived` AS `isReceived`,
`t`.`isRaid` AS `isVirtualStock`,
`e`.`id` AS `entryFk`
`e`.`id` AS `entryFk`,
`t`.`availabled`
FROM (
(
`vn`.`buy` `b`

View File

@ -18,5 +18,6 @@ AS SELECT `p`.`id` AS `Id_Cubo`,
`p`.`base` AS `Base`,
`p`.`isBox` AS `box`,
`p`.`returnCost` AS `costeRetorno`,
`p`.`isActive` AS `isActive`
`p`.`isActive` AS `isActive`,
`p`.`flippingCost` AS `flippingCost`
FROM `vn`.`packaging` `p`

View File

@ -1,8 +0,0 @@
CREATE OR REPLACE DEFINER=`root`@`localhost`
SQL SECURITY DEFINER
VIEW `vn2008`.`Split_lines`
AS SELECT `sl`.`id` AS `Id_Split_lines`,
`sl`.`splitFk` AS `Id_Split`,
`sl`.`itemFk` AS `Id_Article`,
`sl`.`buyFk` AS `Id_Compra`
FROM `vn`.`splitLine` `sl`

View File

@ -29,5 +29,6 @@ AS SELECT `a`.`id` AS `id`,
`a`.`invoiceInPaletizedFk` AS `invoiceInPaletizedFk`,
`a`.`observation` AS `observation`,
`a`.`hasFreightPrepaid` AS `hasFreightPrepaid`,
`a`.`propertyNumber` AS `propertyNumber`
`a`.`propertyNumber` AS `propertyNumber`,
`a`.`costPerKg` AS `costPerKg`
FROM `vn`.`awb` `a`

View File

@ -0,0 +1,27 @@
ALTER TABLE vn.business
ADD CONSTRAINT `business_companyCodeFk` FOREIGN KEY (`companyCodeFk`) REFERENCES `company` (`code`) ON DELETE CASCADE ON UPDATE CASCADE;
-- Auto-generated SQL script. Actual values for binary/complex data types may differ - what you see is the default string representation of values.
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('BusinessReasonEnd','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('CalendarType','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('OccupationCode','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('BusinessReasonEnd','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('WorkerBusinessProfessionalCategory','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('WorkerBusinessAgreement','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('WorkerBusinessType','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('PayrollCategory','find','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Worker','__get__business','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Worker','__create__business','*','ALLOW','ROLE','hr');
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Business','crud','*','ALLOW','ROLE','hr');

View File

@ -0,0 +1,14 @@
UPDATE vn.state
SET alertLevel = 1 -- ON_PREVIOUS
WHERE id IN (
36, -- Previa Revisando
37, -- Previa Revisado
26, -- Prep Previa
28, -- Previa OK
29, -- Previa Impreso
31, -- Polizon Impreso
32, -- Polizon OK
20, -- Asignado
23, -- URGENTE
33 -- Auto_Impreso
);

View File

@ -0,0 +1,10 @@
UPDATE vn.town t
LEFT JOIN vn.zoneGeo zg ON zg.id = t.geoFk
SET t.geoFk = NULL
WHERE zg.id IS NULL;
ALTER TABLE vn.town
ADD CONSTRAINT town_zoneGeo_FK FOREIGN KEY (geoFk)
REFERENCES vn.zoneGeo(id)
ON DELETE RESTRICT
ON UPDATE CASCADE;

View File

@ -0,0 +1,10 @@
UPDATE vn.postCode pc
LEFT JOIN vn.zoneGeo zg ON zg.id = pc.geoFk
SET pc.geoFk = NULL
WHERE zg.id IS NULL;
ALTER TABLE vn.postCode
ADD CONSTRAINT postCode_zoneGeo_FK FOREIGN KEY (geoFk)
REFERENCES vn.zoneGeo(id)
ON DELETE RESTRICT
ON UPDATE CASCADE;

View File

@ -0,0 +1,10 @@
UPDATE vn.province p
LEFT JOIN vn.zoneGeo zg ON zg.id = p.geoFk
SET p.geoFk = NULL
WHERE zg.id IS NULL;
ALTER TABLE vn.province
ADD CONSTRAINT province_zoneGeo_FK FOREIGN KEY (geoFk)
REFERENCES vn.zoneGeo(id)
ON DELETE RESTRICT
ON UPDATE CASCADE;

View File

@ -0,0 +1,19 @@
INSERT INTO account.`role` (name,description,hasLogin)
VALUES ('deliveryFreelancer','Repartidor autónomo',1);
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES
('Route', 'getTickets', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('AgencyTerm', 'filter', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route', 'summary', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route', 'getRouteByAgency', 'WRITE', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route','filter','READ','ALLOW','ROLE','deliveryFreelancer'),
('UserConfig','getUserConfig','*','ALLOW','ROLE','deliveryFreelancer'),
('Route', 'getTickets', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route','guessPriority','WRITE','ALLOW','ROLE','deliveryFreelancer'),
('Route','getDeliveryPoint','READ','ALLOW','ROLE','deliveryFreelancer'),
('Route', 'findById', 'READ', 'ALLOW', 'ROLE', 'deliveryFreelancer'),
('Route','sendSms','WRITE','ALLOW','ROLE','deliveryFreelancer'),
('Ticket','updateAttributes','WRITE','ALLOW','ROLE','deliveryFreelancer'),
('Client','findById','READ','ALLOW','ROLE','deliveryFreelancer');
;

View File

@ -0,0 +1,13 @@
use `vn`;
DELETE ai from
`vn`.`agencyIncoming` ai
LEFT JOIN `vn`.`agencyMode` am ON
am.id = ai.agencyModeFk
WHERE am.id IS null;
ALTER TABLE `vn`.`agencyIncoming`
ADD CONSTRAINT `fk_agencyIncoming_agencyMode`
FOREIGN KEY (`agencyModeFk`)
REFERENCES `agencyMode`(`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;

View File

@ -0,0 +1,7 @@
ALTER TABLE `vn`.`travelThermograph`
ADD COLUMN `agencyModeFk` INT(11) NULL AFTER `editorFk`,
ADD CONSTRAINT `travelThermograph_agencyIncoming_fk`
FOREIGN KEY (`agencyModeFk`)
REFERENCES `agencyIncoming`(`agencyModeFk`)
ON DELETE RESTRICT
ON UPDATE CASCADE;

View File

@ -0,0 +1,4 @@
ALTER TABLE vn.roadmap
DROP FOREIGN KEY roadmap_worker_FK_2,
DROP FOREIGN KEY roadmap_worker_FK,
DROP FOREIGN KEY roadmap_ibfk_2;

View File

@ -0,0 +1,20 @@
ALTER TABLE vn.roadmap
COMMENT='Rutas troncales (trailers)',
MODIFY COLUMN m3 int(10) unsigned DEFAULT NULL NULL COMMENT 'Capacidad máxima del remolque',
MODIFY COLUMN trailerPlate varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL,
MODIFY COLUMN etd datetime NOT NULL COMMENT 'Tiempo estimado de salida',
MODIFY COLUMN `name` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL,
MODIFY COLUMN driver1Fk int(10) unsigned DEFAULT NULL NULL AFTER driverName,
MODIFY COLUMN driver2Fk int(10) unsigned DEFAULT NULL NULL AFTER driver1Fk,
ADD eta datetime DEFAULT NULL NULL COMMENT 'Tiempo estimado de llegada' AFTER etd,
ADD roadmapAddressFk int(11) DEFAULT NULL NULL AFTER `name`,
ADD dollyPlate varchar(10) DEFAULT NULL AFTER trailerPlate,
ADD tugPlate varchar(10) DEFAULT NULL AFTER dollyPlate,
ADD driverChangeName varchar(45) DEFAULT NULL AFTER driver2Fk,
ADD driverChangeFk int(10) unsigned DEFAULT NULL NULL AFTER driverChangeName;
-- Separamos los CHANGE por que si no arriba no se aplican
ALTER TABLE vn.roadmap
CHANGE userFk editorFk int(10) unsigned DEFAULT NULL NULL AFTER m3;
CREATE INDEX roadmap_etd_IDX USING BTREE ON vn.roadmap (etd);

View File

@ -0,0 +1,15 @@
UPDATE vn.roadmap
SET roadmapAddressFk = (SELECT MIN(addressFk) FROM vn.roadmapAddress),
eta = etd + INTERVAL 1 DAY;
ALTER TABLE vn.roadmap
ADD CONSTRAINT roadmap_roadmapAddress_FK FOREIGN KEY (roadmapAddressFk)
REFERENCES vn.roadmapAddress(addressFk) ON DELETE RESTRICT ON UPDATE CASCADE,
ADD CONSTRAINT roadmap_driver_FK FOREIGN KEY (driver1Fk)
REFERENCES vn.worker(id) ON DELETE RESTRICT ON UPDATE CASCADE,
ADD CONSTRAINT roadmap_driver_FK2 FOREIGN KEY (driver2Fk)
REFERENCES vn.worker(id) ON DELETE RESTRICT ON UPDATE CASCADE,
ADD CONSTRAINT roadmap_driverChange_FK FOREIGN KEY (driverChangeFk)
REFERENCES vn.worker(id) ON DELETE RESTRICT ON UPDATE CASCADE,
ADD CONSTRAINT roadmap_user_Fk FOREIGN KEY (editorFk)
REFERENCES account.user(id) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,7 @@
ALTER TABLE vn.roadmapStop
CHANGE userFk editorFk int(10) unsigned DEFAULT NULL NULL,
CHANGE addressFk roadmapAddressFk int(11) DEFAULT NULL NULL,
DROP FOREIGN KEY expeditionTruck_FK_2;
ALTER TABLE vn.roadmapStop ADD CONSTRAINT roadmapStop_roadmap_FK
FOREIGN KEY (roadmapFk) REFERENCES vn.roadmap(id) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -0,0 +1,4 @@
ALTER TABLE vn.route
ADD roadmapStopFk int(11) NULL,
ADD CONSTRAINT route_roadmapStop_FK FOREIGN KEY (roadmapStopFk) REFERENCES vn.roadmapStop(id) ON DELETE RESTRICT ON UPDATE CASCADE,
CHANGE editorFk editorFk int(10) unsigned DEFAULT NULL NULL AFTER roadmapStopFk;

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.roadmapAddress
COMMENT='Direcciones de los troncales o también llamados puntos de distribución';

View File

@ -0,0 +1,11 @@
GRANT SELECT ON TABLE vn.roadmap TO 'delivery';
GRANT SELECT ON TABLE vn.roadmapStop TO 'delivery';
GRANT SELECT ON TABLE vn.roadmapAddress TO 'delivery';
GRANT DELETE, UPDATE, INSERT ON TABLE vn.roadmap TO 'deliveryBoss';
GRANT DELETE, UPDATE, INSERT ON TABLE vn.roadmapStop TO 'deliveryBoss';
GRANT DELETE, UPDATE, INSERT ON TABLE vn.roadmapAddress TO 'deliveryBoss';
-- Comentado debido a que da error porque ejecuta primero el script de la versión
-- GRANT EXECUTE ON PROCEDURE vn.roadmap_cloneDay TO 'deliveryBoss';
-- GRANT EXECUTE ON FUNCTION vn.getTimeBetweenRoadmapAddresses TO 'deliveryBoss';

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.route DROP FOREIGN KEY fk_route_1;
ALTER TABLE vn.route DROP COLUMN zoneFk;

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.vehicle
ADD typeFk enum('car','van','truck','trailer','tug', 'tugDolly','dolly') DEFAULT 'van' NOT NULL;

View File

@ -0,0 +1 @@
CREATE INDEX route_dated_IDX USING BTREE ON vn.route (dated);

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`tag`
ADD COLUMN IF NOT EXISTS `validationRegex` varchar(50) DEFAULT NULL;

View File

@ -0,0 +1,5 @@
RENAME TABLE vn.sorter TO vn.sorter__;
ALTER TABLE vn.sorter__ COMMENT='@deprecated 2025-01-22';
RENAME TABLE vn.splitLine TO vn.splitLine__;
ALTER TABLE vn.splitLine__ COMMENT='@deprecated 2025-01-22';

View File

@ -0,0 +1,3 @@
-- Place your SQL code here
ALTER TABLE vn.travel ADD IF NOT EXISTS availabled DATETIME NULL
COMMENT 'Indicates the moment in time when the goods become available for picking';

View File

@ -0,0 +1,2 @@
INSERT IGNORE INTO salix.ACL (model, property, accessType, permission, principalType, principalId)
VALUES('Entry', 'transfer', 'WRITE', 'ALLOW', 'ROLE', 'coolerBoss');

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.packaging
ADD COLUMN flippingCost decimal(10, 2) NOT NULL DEFAULT 0.00

View File

@ -0,0 +1 @@
ALTER TABLE vn.travel CHANGE appointment appointment__ datetime DEFAULT NULL COMMENT '@deprecated 2025-01-28';

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`awb`
ADD COLUMN `costPerKg` DECIMAL(10, 2) UNSIGNED DEFAULT NULL COMMENT 'Tarifa que indica a cuanto cuesta el kilo en ese vuelo';

View File

@ -0,0 +1,5 @@
INSERT IGNORE INTO util.notification
SET name = 'misallocation-warehouse',
description = 'Misallocation in warehouse';

View File

@ -1,65 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Client defaulter path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('insurance', 'client');
await page.accessToSection('client.defaulter');
});
afterAll(async() => {
await browser.close();
});
it('should count the amount of clients in the turns section', async() => {
const result = await page.countElement(selectors.clientDefaulter.anyClient);
expect(result).toEqual(6);
});
it('should check contain expected client', async() => {
const clientName =
await page.waitToGetProperty(selectors.clientDefaulter.firstClientName, 'innerText');
const salesPersonName =
await page.waitToGetProperty(selectors.clientDefaulter.firstSalesPersonName, 'innerText');
expect(clientName).toEqual('Ororo Munroe');
expect(salesPersonName).toEqual('salesperson');
});
it('should first observation not changed', async() => {
const expectedObservation = 'Madness, as you know, is like gravity, all it takes is a little push';
const result = await page.waitToGetProperty(selectors.clientDefaulter.firstObservation, 'value');
expect(result).toContain(expectedObservation);
});
it('should not add empty observation', async() => {
await page.waitToClick(selectors.clientDefaulter.allDefaulterCheckbox);
await page.waitToClick(selectors.clientDefaulter.addObservationButton);
await page.write(selectors.clientDefaulter.observation, '');
await page.waitToClick(selectors.clientDefaulter.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`The message can't be empty`);
});
it('should checked all defaulters', async() => {
await page.loginAndModule('insurance', 'client');
await page.accessToSection('client.defaulter');
await page.waitToClick(selectors.clientDefaulter.allDefaulterCheckbox);
});
it('should add observation for all clients', async() => {
await page.waitToClick(selectors.clientDefaulter.addObservationButton);
await page.write(selectors.clientDefaulter.observation, 'My new observation');
await page.waitToClick(selectors.clientDefaulter.saveButton);
});
});

View File

@ -1,400 +1,401 @@
{
"Phone format is invalid": "El formato del teléfono no es correcto",
"You are not allowed to change the credit": "No tienes privilegios para modificar el crédito",
"Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia",
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
"Can't be blank": "No puede estar en blanco",
"Invalid TIN": "NIF/CIF inválido",
"TIN must be unique": "El NIF/CIF debe ser único",
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
"Is invalid": "Es inválido",
"Quantity cannot be zero": "La cantidad no puede ser cero",
"Enter an integer different to zero": "Introduce un entero distinto de cero",
"Package cannot be blank": "El embalaje no puede estar en blanco",
"The company name must be unique": "La razón social debe ser única",
"Invalid email": "Correo electrónico inválido",
"The IBAN does not have the correct format": "El IBAN no tiene el formato correcto",
"That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN",
"That payment method requires a BIC": "El método de pago seleccionado requiere un BIC",
"State cannot be blank": "El estado no puede estar en blanco",
"Worker cannot be blank": "El trabajador no puede estar en blanco",
"Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado",
"can't be blank": "El campo no puede estar vacío",
"Observation type must be unique": "El tipo de observación no puede repetirse",
"The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero",
"The grade must be similar to the last one": "El grade debe ser similar al último",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco",
"Period cannot be blank": "El periodo no puede estar en blanco",
"Choose a company": "Selecciona una empresa",
"Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto",
"Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres",
"Cannot be blank": "El campo no puede estar en blanco",
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
"Description cannot be blank": "Se debe rellenar el campo de texto",
"The price of the item changed": "El precio del artículo cambió",
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
"The value should be a number": "El valor debe ser un numero",
"This order is not editable": "Esta orden no se puede modificar",
"You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado",
"You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda",
"is not a valid date": "No es una fecha valida",
"Barcode must be unique": "El código de barras debe ser único",
"The warehouse can't be repeated": "El almacén no puede repetirse",
"The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item",
"The observation type can't be repeated": "El tipo de observación no puede repetirse",
"A claim with that sale already exists": "Ya existe una reclamación para esta línea",
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco",
"Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe",
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
"You don't have enough privileges": "No tienes suficientes permisos",
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos",
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ",
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
"You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo",
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
"ORDER_EMPTY": "Cesta vacía",
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
"Street cannot be empty": "Dirección no puede estar en blanco",
"City cannot be empty": "Ciudad no puede estar en blanco",
"Code cannot be blank": "Código no puede estar en blanco",
"You cannot remove this department": "No puedes eliminar este departamento",
"The extension must be unique": "La extensión debe ser unica",
"The secret can't be blank": "La contraseña no puede estar en blanco",
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado",
"You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado",
"The introduced hour already exists": "Esta hora ya ha sido introducida",
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes",
"The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas",
"NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros",
"ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado",
"The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)",
"Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"This item doesn't exists": "El artículo no existe",
"NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"Extension format is invalid": "El formato de la extensión es inválido",
"Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket",
"This item is not available": "Este artículo no está disponible",
"This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "El concepto no puede quedar en blanco",
"File doesn't exists": "El archivo no existe",
"You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias",
"This ticket is already on weekly tickets": "Este ticket ya está en tickets programados",
"Ticket id cannot be blank": "El id de ticket no puede quedar en blanco",
"Weekday cannot be blank": "El día de la semana no puede quedar en blanco",
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
"Invalid quantity": "Cantidad invalida",
"This postal code is not valid": "Este código postal no es válido",
"is invalid": "es inválido",
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
"This phone already exists": "Este teléfono ya existe",
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
"You should specify a date": "Debes especificar una fecha",
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin",
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin",
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
"Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}",
"Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changed sale quantity": "He cambiado {{changes}} del ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changes in sales": "la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}*",
"State": "Estado",
"regular": "normal",
"reserved": "reservado",
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
"Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}",
"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}}})",
"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}}*, con el tipo de recogida *{{claimPickup}}*",
"Claim state has changed to": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *{{newState}}*",
"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 4000": "La distancia debe ser inferior a 4000",
"This ticket is deleted": "Este ticket está eliminado",
"Unable to clone this travel": "No ha sido posible clonar este travel",
"This thermograph id already exists": "La id del termógrafo ya existe",
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
"ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA",
"Invalid password": "Invalid password",
"Password does not meet requirements": "La contraseña no cumple los requisitos",
"Role already assigned": "Rol ya asignado",
"Invalid role name": "Nombre de rol no válido",
"Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase",
"Email already exists": "El correo ya existe",
"User already exists": "El/La usuario/a ya existe",
"Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral",
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"agencyModeFk": "Agencia",
"clientFk": "Cliente",
"zoneFk": "Zona",
"warehouseFk": "Almacén",
"shipped": "F. envío",
"landed": "F. entrega",
"addressFk": "Consignatario",
"companyFk": "Empresa",
"agency": "Agencia",
"delivery": "Reparto",
"The social name cannot be empty": "La razón social no puede quedar en blanco",
"The nif cannot be empty": "El NIF no puede quedar en blanco",
"You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados",
"ASSIGN_ZONE_FIRST": "Asigna una zona primero",
"Amount cannot be zero": "El importe no puede ser cero",
"Company has to be official": "Empresa inválida",
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
"This BIC already exist.": "Este BIC ya existe.",
"That item doesn't exists": "Ese artículo no existe",
"There's a new urgent ticket:": "Hay un nuevo ticket urgente:",
"Invalid account": "Cuenta inválida",
"Compensation account is empty": "La cuenta para compensar está vacia",
"This genus already exist": "Este genus ya existe",
"This specie already exist": "Esta especie ya existe",
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "Ninguno",
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada",
"Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'",
"This document already exists on this ticket": "Este documento ya existe en el ticket",
"Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables",
"You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes",
"nickname": "nickname",
"INACTIVE_PROVIDER": "Proveedor inactivo",
"This client is not invoiceable": "Este cliente no es facturable",
"serial non editable": "Esta serie no permite asignar la referencia",
"Max shipped required": "La fecha límite es requerida",
"Can't invoice to future": "No se puede facturar a futuro",
"Can't invoice to past": "No se puede facturar a pasado",
"This ticket is already invoiced": "Este ticket ya está facturado",
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas",
"Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exist": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
"The account size must be exactly 10 characters": "El tamaño de la cuenta debe ser exactamente de 10 caracteres",
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
"You don't have privileges to create refund": "No tienes permisos para crear un abono",
"The item is required": "El artículo es requerido",
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "Sin negativos",
"routeFk": "routeFk",
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
"No hay un contrato en vigor": "No hay un contrato en vigor",
"No se permite fichar a futuro": "No se permite fichar a futuro",
"No está permitido trabajar": "No está permitido trabajar",
"Fichadas impares": "Fichadas impares",
"Descanso diario 12h.": "Descanso diario 12h.",
"Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.",
"Dirección incorrecta": "Dirección incorrecta",
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe",
"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 [{{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",
"Negative basis": "Base negativa",
"This worker code already exists": "Este codigo de trabajador ya existe",
"This personal mail already exists": "Este correo personal ya existe",
"This worker already exists": "Este trabajador ya existe",
"App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload delivery note": "Error al subir albarán {{id}}",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
"There is no assigned email for this client": "No hay correo asignado para este cliente",
"Exists an invoice with a future date": "Existe una factura con fecha posterior",
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
"Warehouse inventory not set": "El almacén inventario no está establecido",
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %s",
"Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}} {{defaulterId}} ({{{defaulterUrl}}})",
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
"Invalid auth code": "Código de verificación incorrecto",
"Invalid or expired verification code": "Código de verificación incorrecto o expirado",
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
"company": "Compañía",
"country": "País",
"clientId": "Id cliente",
"clientSocialName": "Cliente",
"amount": "Importe",
"taxableBase": "Base",
"ticketFk": "Id ticket",
"isActive": "Activo",
"hasToInvoice": "Facturar",
"isTaxDataChecked": "Datos comprobados",
"comercialId": "Id comercial",
"comercialName": "Comercial",
"Pass expired": "La contraseña ha caducado, cambiela desde Salix",
"Invalid NIF for VIES": "Invalid NIF for VIES",
"Ticket does not exist": "Este ticket no existe",
"Ticket is already signed": "Este ticket ya ha sido firmado",
"Authentication failed": "Autenticación fallida",
"You can't use the same password": "No puedes usar la misma contraseña",
"You can only add negative amounts in refund tickets": "Solo se puede añadir cantidades negativas en tickets abono",
"Fecha fuera de rango": "Fecha fuera de rango",
"Error while generating PDF": "Error al generar PDF",
"Error when sending mail to client": "Error al enviar el correo al cliente",
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d",
"hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
"hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
"Ticket without Route": "Ticket sin ruta",
"Select a different client": "Seleccione un cliente distinto",
"Fill all the fields": "Rellene todos los campos",
"The response is not a PDF": "La respuesta no es un PDF",
"Booking completed": "Reserva completada",
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
"User disabled": "Usuario desactivado",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
"It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
"This claim has been updated": "La reclamación con Id: {{claimId}}, ha sido actualizada",
"This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
"Field are invalid": "El campo '{{tag}}' no es válido",
"Incorrect pin": "Pin incorrecto.",
"You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar que cumplan los requisitos de facturación",
"this warehouse has not dms": "El Almacén no acepta documentos",
"This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado",
"Name should be uppercase": "El nombre debe ir en mayúscula",
"Bank entity must be specified": "La entidad bancaria es obligatoria",
"An email is necessary": "Es necesario un email",
"You cannot update these fields": "No puedes actualizar estos campos",
"CountryFK cannot be empty": "El país no puede estar vacío",
"Cmr file does not exist": "El archivo del cmr no existe",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
"The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas",
"No invoice series found for these parameters": "No se encontró una serie para estos parámetros",
"The line could not be marked": "La linea no puede ser marcada",
"Through this procedure, it is not possible to modify the password of users with verified email": "Mediante este procedimiento, no es posible modificar la contraseña de usuarios con correo verificado",
"They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados",
"InvoiceIn is already booked": "La factura recibida está contabilizada",
"This workCenter is already assigned to this agency": "Este centro de trabajo ya está asignado a esta agencia",
"Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura",
"Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE",
"You can not use the same password": "No puedes usar la misma contraseña",
"This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
"You can only have one PDA": "Solo puedes tener un PDA",
"The invoices have been created but the PDFs could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
"Payment method is required": "El método de pago es obligatorio",
"Cannot send mail": "Não é possível enviar o email",
"CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos",
"The sale not exists in the item shelving": "La venta no existe en la estantería del artículo",
"The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada",
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe",
"Weight already set": "El peso ya está establecido",
"This ticket is not allocated to your department": "Este ticket no está asignado a tu departamento",
"There is already a tray with the same height": "Ya existe una bandeja con la misma altura",
"The height must be greater than 50cm": "La altura debe ser superior a 50cm",
"The maximum height of the wagon is 200cm": "La altura máxima es 200cm",
"The entry does not have stickers": "La entrada no tiene etiquetas",
"This buyer has already made a reservation for this date": "Este comprador ya ha hecho una reserva para esta fecha",
"No valid travel thermograph found": "No se encontró un termógrafo válido",
"The quantity claimed cannot be greater than the quantity of the line": "La cantidad reclamada no puede ser mayor que la cantidad de la línea",
"type cannot be blank": "Se debe rellenar el tipo",
"There are tickets for this area, delete them first": "Hay tickets para esta sección, borralos primero",
"There is no company associated with that warehouse": "No hay ninguna empresa asociada a ese almacén",
"You do not have permission to modify the booked field": "No tienes permisos para modificar el campo contabilizada",
"ticketLostExpedition": "El ticket [{{ticketId}}]({{{ticketUrl}}}) tiene la siguiente expedición perdida:{{ expeditionId }}",
"The web user's email already exists": "El correo del usuario web ya existe",
"Sales already moved": "Ya han sido transferidas",
"The raid information is not correct": "La información de la redada no es correcta",
"An item type with the same code already exists": "Un tipo con el mismo código ya existe",
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles",
"All tickets have a route order": "Todos los tickets tienen orden de ruta",
"There are tickets to be invoiced": "La zona tiene tickets por facturar",
"Incorrect delivery order alert on route": "Alerta de orden de entrega incorrecta en ruta: {{ route }} zona: {{ zone }}",
"Ticket has been delivered out of order": "El ticket {{ticket}} de la ruta {{{fullUrl}}} no ha sigo entregado en su orden.",
"Price cannot be blank": "El precio no puede estar en blanco"
}
"Phone format is invalid": "El formato del teléfono no es correcto",
"You are not allowed to change the credit": "No tienes privilegios para modificar el crédito",
"Unable to mark the equivalence surcharge": "No se puede marcar el recargo de equivalencia",
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
"Can't be blank": "No puede estar en blanco",
"Invalid TIN": "NIF/CIF inválido",
"TIN must be unique": "El NIF/CIF debe ser único",
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
"Is invalid": "Es inválido",
"Quantity cannot be zero": "La cantidad no puede ser cero",
"Enter an integer different to zero": "Introduce un entero distinto de cero",
"Package cannot be blank": "El embalaje no puede estar en blanco",
"The company name must be unique": "La razón social debe ser única",
"Invalid email": "Correo electrónico inválido",
"The IBAN does not have the correct format": "El IBAN no tiene el formato correcto",
"That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN",
"That payment method requires a BIC": "El método de pago seleccionado requiere un BIC",
"State cannot be blank": "El estado no puede estar en blanco",
"Worker cannot be blank": "El trabajador no puede estar en blanco",
"Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado",
"can't be blank": "El campo no puede estar vacío",
"Observation type must be unique": "El tipo de observación no puede repetirse",
"The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero",
"The grade must be similar to the last one": "El grade debe ser similar al último",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco",
"Period cannot be blank": "El periodo no puede estar en blanco",
"Choose a company": "Selecciona una empresa",
"Se debe rellenar el campo de texto": "Se debe rellenar el campo de texto",
"Description should have maximum of 45 characters": "La descripción debe tener maximo 45 caracteres",
"Cannot be blank": "El campo no puede estar en blanco",
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
"Description cannot be blank": "Se debe rellenar el campo de texto",
"The price of the item changed": "El precio del artículo cambió",
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
"The value should be a number": "El valor debe ser un numero",
"This order is not editable": "Esta orden no se puede modificar",
"You can't create an order for a frozen client": "No puedes crear una orden para un cliente congelado",
"You can't create an order for a client that has a debt": "No puedes crear una orden para un cliente con deuda",
"is not a valid date": "No es una fecha valida",
"Barcode must be unique": "El código de barras debe ser único",
"The warehouse can't be repeated": "El almacén no puede repetirse",
"The tag or priority can't be repeated for an item": "El tag o prioridad no puede repetirse para un item",
"The observation type can't be repeated": "El tipo de observación no puede repetirse",
"A claim with that sale already exists": "Ya existe una reclamación para esta línea",
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco",
"Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe",
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
"You don't have enough privileges": "No tienes suficientes permisos",
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos",
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ",
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
"You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo",
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
"ORDER_EMPTY": "Cesta vacía",
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
"Street cannot be empty": "Dirección no puede estar en blanco",
"City cannot be empty": "Ciudad no puede estar en blanco",
"Code cannot be blank": "Código no puede estar en blanco",
"You cannot remove this department": "No puedes eliminar este departamento",
"The extension must be unique": "La extensión debe ser unica",
"The secret can't be blank": "La contraseña no puede estar en blanco",
"We weren't able to send this SMS": "No hemos podido enviar el SMS",
"This client can't be invoiced": "Este cliente no puede ser facturado",
"You must provide the correction information to generate a corrective invoice": "Debes informar la información de corrección para generar una factura rectificativa",
"This ticket can't be invoiced": "Este ticket no puede ser facturado",
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado",
"The introduced hour already exists": "Esta hora ya ha sido introducida",
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes",
"The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas",
"NO_AGENCY_AVAILABLE": "No hay una zona de reparto disponible con estos parámetros",
"ERROR_PAST_SHIPMENT": "No puedes seleccionar una fecha de envío en pasado",
"The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales do not exists": "La(s) línea(s) seleccionada(s) no existe(n)",
"Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"This item doesn't exists": "El artículo no existe",
"NOT_ZONE_WITH_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",
"Extension format is invalid": "El formato de la extensión es inválido",
"Invalid parameters to create a new ticket": "Parámetros inválidos para crear un nuevo ticket",
"This item is not available": "Este artículo no está disponible",
"This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "El concepto no puede quedar en blanco",
"File doesn't exists": "El archivo no existe",
"You don't have privileges to change the zone": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias",
"This ticket is already on weekly tickets": "Este ticket ya está en tickets programados",
"Ticket id cannot be blank": "El id de ticket no puede quedar en blanco",
"Weekday cannot be blank": "El día de la semana no puede quedar en blanco",
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
"Invalid quantity": "Cantidad invalida",
"This postal code is not valid": "Este código postal no es válido",
"is invalid": "es inválido",
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
"This phone already exists": "Este teléfono ya existe",
"You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos",
"You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado",
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
"You should specify a date": "Debes especificar una fecha",
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin",
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin",
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
"Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}",
"Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}",
"Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changed sale quantity": "He cambiado {{changes}} del ticket [{{ticketId}}]({{{ticketUrl}}})",
"Changes in sales": "la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}*",
"State": "Estado",
"regular": "normal",
"reserved": "reservado",
"Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})",
"Deny buy request": "Se ha rechazado la petición de compra para el ticket id [{{ticketId}}]({{{url}}}). Motivo: {{observation}}",
"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}}})",
"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}}*, con el tipo de recogida *{{claimPickup}}*",
"Claim state has changed to": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *{{newState}}*",
"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 4000": "La distancia debe ser inferior a 4000",
"This ticket is deleted": "Este ticket está eliminado",
"Unable to clone this travel": "No ha sido posible clonar este travel",
"This thermograph id already exists": "La id del termógrafo ya existe",
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
"ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA",
"Invalid password": "Invalid password",
"Password does not meet requirements": "La contraseña no cumple los requisitos",
"Role already assigned": "Rol ya asignado",
"Invalid role name": "Nombre de rol no válido",
"Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase",
"Email already exists": "El correo ya existe",
"User already exists": "El/La usuario/a ya existe",
"Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral",
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
"I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
"agencyModeFk": "Agencia",
"clientFk": "Cliente",
"zoneFk": "Zona",
"warehouseFk": "Almacén",
"shipped": "F. envío",
"landed": "F. entrega",
"addressFk": "Consignatario",
"companyFk": "Empresa",
"agency": "Agencia",
"delivery": "Reparto",
"The social name cannot be empty": "La razón social no puede quedar en blanco",
"The nif cannot be empty": "El NIF no puede quedar en blanco",
"You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados",
"ASSIGN_ZONE_FIRST": "Asigna una zona primero",
"Amount cannot be zero": "El importe no puede ser cero",
"Company has to be official": "Empresa inválida",
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
"This BIC already exist.": "Este BIC ya existe.",
"That item doesn't exists": "Ese artículo no existe",
"There's a new urgent ticket:": "Hay un nuevo ticket urgente:",
"Invalid account": "Cuenta inválida",
"Compensation account is empty": "La cuenta para compensar está vacia",
"This genus already exist": "Este genus ya existe",
"This specie already exist": "Esta especie ya existe",
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "Ninguno",
"The contract was not active during the selected date": "El contrato no estaba activo durante la fecha seleccionada",
"Cannot add more than one '1/2 day vacation'": "No puedes añadir más de un 'Vacaciones 1/2 dia'",
"This document already exists on this ticket": "Este documento ya existe en el ticket",
"Some of the selected tickets are not billable": "Algunos de los tickets seleccionados no son facturables",
"You can't invoice tickets from multiple clients": "No puedes facturar tickets de multiples clientes",
"nickname": "nickname",
"INACTIVE_PROVIDER": "Proveedor inactivo",
"This client is not invoiceable": "Este cliente no es facturable",
"serial non editable": "Esta serie no permite asignar la referencia",
"Max shipped required": "La fecha límite es requerida",
"Can't invoice to future": "No se puede facturar a futuro",
"Can't invoice to past": "No se puede facturar a pasado",
"This ticket is already invoiced": "Este ticket ya está facturado",
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
"You can't change the credit set to zero from a financialBoss": "No puedes cambiar el cŕedito establecido a cero por un jefe de finanzas",
"Amounts do not match": "Las cantidades no coinciden",
"The PDF document does not exist": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'",
"The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos",
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
"The account size must be exactly 10 characters": "El tamaño de la cuenta debe ser exactamente de 10 caracteres",
"Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
"You don't have privileges to create refund": "No tienes permisos para crear un abono",
"The item is required": "El artículo es requerido",
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "Sin negativos",
"routeFk": "routeFk",
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
"No hay un contrato en vigor": "No hay un contrato en vigor",
"No se permite fichar a futuro": "No se permite fichar a futuro",
"No está permitido trabajar": "No está permitido trabajar",
"Fichadas impares": "Fichadas impares",
"Descanso diario 12h.": "Descanso diario 12h.",
"Descanso semanal 36h. / 72h.": "Descanso semanal 36h. / 72h.",
"Dirección incorrecta": "Dirección incorrecta",
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
"This route does not exists": "Esta ruta no existe",
"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 [{{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",
"Negative basis": "Base negativa",
"This worker code already exists": "Este codigo de trabajador ya existe",
"This personal mail already exists": "Este correo personal ya existe",
"This worker already exists": "Este trabajador ya existe",
"App name does not exist": "El nombre de aplicación no es válido",
"Try again": "Vuelve a intentarlo",
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
"Failed to upload delivery note": "Error al subir albarán {{id}}",
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
"There is no assigned email for this client": "No hay correo asignado para este cliente",
"Exists an invoice with a future date": "Existe una factura con fecha posterior",
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
"Warehouse inventory not set": "El almacén inventario no está establecido",
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº %s",
"Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}} {{defaulterId}} ({{{defaulterUrl}}})",
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
"Invalid auth code": "Código de verificación incorrecto",
"Invalid or expired verification code": "Código de verificación incorrecto o expirado",
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
"company": "Compañía",
"country": "País",
"clientId": "Id cliente",
"clientSocialName": "Cliente",
"amount": "Importe",
"taxableBase": "Base",
"ticketFk": "Id ticket",
"isActive": "Activo",
"hasToInvoice": "Facturar",
"isTaxDataChecked": "Datos comprobados",
"comercialId": "Id comercial",
"comercialName": "Comercial",
"Pass expired": "La contraseña ha caducado, cambiela desde Salix",
"Invalid NIF for VIES": "Invalid NIF for VIES",
"Ticket does not exist": "Este ticket no existe",
"Ticket is already signed": "Este ticket ya ha sido firmado",
"Authentication failed": "Autenticación fallida",
"You can't use the same password": "No puedes usar la misma contraseña",
"You can only add negative amounts in refund tickets": "Solo se puede añadir cantidades negativas en tickets abono",
"Fecha fuera de rango": "Fecha fuera de rango",
"Error while generating PDF": "Error al generar PDF",
"Error when sending mail to client": "Error al enviar el correo al cliente",
"Mail not sent": "Se ha producido un fallo al enviar la factura al cliente [{{clientId}}]({{{clientUrl}}}), por favor revisa la dirección de correo electrónico",
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d",
"hasAnyNegativeBase": "Base negativa para los tickets: {{ticketsIds}}",
"hasAnyPositiveBase": "Base positivas para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
"Ticket without Route": "Ticket sin ruta",
"Select a different client": "Seleccione un cliente distinto",
"Fill all the fields": "Rellene todos los campos",
"The response is not a PDF": "La respuesta no es un PDF",
"Booking completed": "Reserva completada",
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
"User disabled": "Usuario desactivado",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
"It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
"This claim has been updated": "La reclamación con Id: {{claimId}}, ha sido actualizada",
"This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
"Field are invalid": "El campo '{{tag}}' no es válido",
"Incorrect pin": "Pin incorrecto.",
"You already have the mailAlias": "Ya tienes este alias de correo",
"The alias cant be modified": "Este alias de correo no puede ser modificado",
"No tickets to invoice": "No hay tickets para facturar que cumplan los requisitos de facturación",
"this warehouse has not dms": "El Almacén no acepta documentos",
"This ticket already has a cmr saved": "Este ticket ya tiene un cmr guardado",
"Name should be uppercase": "El nombre debe ir en mayúscula",
"Bank entity must be specified": "La entidad bancaria es obligatoria",
"An email is necessary": "Es necesario un email",
"You cannot update these fields": "No puedes actualizar estos campos",
"CountryFK cannot be empty": "El país no puede estar vacío",
"Cmr file does not exist": "El archivo del cmr no existe",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
"The address of the customer must have information about Incoterms and Customs Agent": "El consignatario del cliente debe tener informado Incoterms y Agente de aduanas",
"No invoice series found for these parameters": "No se encontró una serie para estos parámetros",
"The line could not be marked": "La linea no puede ser marcada",
"Through this procedure, it is not possible to modify the password of users with verified email": "Mediante este procedimiento, no es posible modificar la contraseña de usuarios con correo verificado",
"They're not your subordinate": "No es tu subordinado/a.",
"No results found": "No se han encontrado resultados",
"InvoiceIn is already booked": "La factura recibida está contabilizada",
"This workCenter is already assigned to this agency": "Este centro de trabajo ya está asignado a esta agencia",
"Select ticket or client": "Elija un ticket o un client",
"It was not able to create the invoice": "No se pudo crear la factura",
"Incoterms and Customs agent are required for a non UEE member": "Se requieren Incoterms y agente de aduanas para un no miembro de la UEE",
"You can not use the same password": "No puedes usar la misma contraseña",
"This PDA is already assigned to another user": "Este PDA ya está asignado a otro usuario",
"You can only have one PDA": "Solo puedes tener un PDA",
"The invoices have been created but the PDFs could not be generated": "Se ha facturado pero no se ha podido generar el PDF",
"It has been invoiced but the PDF of refund not be generated": "Se ha facturado pero no se ha podido generar el PDF del abono",
"Payment method is required": "El método de pago es obligatorio",
"Cannot send mail": "Não é possível enviar o email",
"CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos",
"The sale not exists in the item shelving": "La venta no existe en la estantería del artículo",
"The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros",
"Original invoice not found": "Factura original no encontrada",
"The entry has no lines or does not exist": "La entrada no tiene lineas o no existe",
"Weight already set": "El peso ya está establecido",
"This ticket is not allocated to your department": "Este ticket no está asignado a tu departamento",
"There is already a tray with the same height": "Ya existe una bandeja con la misma altura",
"The height must be greater than 50cm": "La altura debe ser superior a 50cm",
"The maximum height of the wagon is 200cm": "La altura máxima es 200cm",
"The entry does not have stickers": "La entrada no tiene etiquetas",
"This buyer has already made a reservation for this date": "Este comprador ya ha hecho una reserva para esta fecha",
"No valid travel thermograph found": "No se encontró un termógrafo válido",
"The quantity claimed cannot be greater than the quantity of the line": "La cantidad reclamada no puede ser mayor que la cantidad de la línea",
"type cannot be blank": "Se debe rellenar el tipo",
"There are tickets for this area, delete them first": "Hay tickets para esta sección, borralos primero",
"There is no company associated with that warehouse": "No hay ninguna empresa asociada a ese almacén",
"You do not have permission to modify the booked field": "No tienes permisos para modificar el campo contabilizada",
"ticketLostExpedition": "El ticket [{{ticketId}}]({{{ticketUrl}}}) tiene la siguiente expedición perdida:{{ expeditionId }}",
"The web user's email already exists": "El correo del usuario web ya existe",
"Sales already moved": "Ya han sido transferidas",
"The raid information is not correct": "La información de la redada no es correcta",
"An item type with the same code already exists": "Un tipo con el mismo código ya existe",
"Holidays to past days not available": "Las vacaciones a días pasados no están disponibles",
"All tickets have a route order": "Todos los tickets tienen orden de ruta",
"There are tickets to be invoiced": "La zona tiene tickets por facturar",
"Incorrect delivery order alert on route": "Alerta de orden de entrega incorrecta en ruta: {{ route }} zona: {{ zone }}",
"Ticket has been delivered out of order": "El ticket {{ticket}} {{{fullUrl}}} no ha sido entregado en su orden.",
"Price cannot be blank": "El precio no puede estar en blanco"
}

View File

@ -109,6 +109,7 @@ module.exports = Self => {
const args = ctx.args;
const myOptions = {};
let to;
let myTeamIds = [];
if (typeof options == 'object')
Object.assign(myOptions, options);
@ -133,21 +134,8 @@ module.exports = Self => {
claimIdsByClaimResponsibleFk = claims.map(claim => claim.claimFk);
}
// Apply filter by team
const teamMembersId = [];
if (args.myTeam != null) {
const worker = await models.Worker.findById(userId, {
include: {
relation: 'collegues'
}
}, myOptions);
const collegues = worker.collegues() || [];
for (let collegue of collegues)
teamMembersId.push(collegue.collegueFk);
if (teamMembersId.length == 0)
teamMembersId.push(userId);
}
if (args.myTeam != null)
myTeamIds = await models.Worker.myTeam(userId);
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
@ -184,9 +172,9 @@ module.exports = Self => {
return {'t.zoneFk': value};
case 'myTeam':
if (value)
return {'cl.workerFk': {inq: teamMembersId}};
return {'cl.workerFk': {inq: myTeamIds}};
else
return {'cl.workerFk': {nin: teamMembersId}};
return {'cl.workerFk': {nin: myTeamIds}};
}
});

View File

@ -158,10 +158,12 @@ module.exports = Self => {
a.provinceFk AS provinceAddressFk,
p.name AS province,
u.id AS salesPersonFk,
u.name AS salesPerson
u.name AS salesPerson,
co.name AS country
FROM client c
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN province p ON p.id = c.provinceFk
LEFT JOIN country co ON co.id = c.countryFk
JOIN address a ON a.clientFk = c.id
`
);

View File

@ -157,4 +157,52 @@ describe('Address updateAddress', () => {
throw e;
}
});
it('should update ticket observations when updateObservations is true', async() => {
const tx = await models.Client.beginTransaction({});
const client = 1103;
const address = 123;
const ticket = 31;
const observationType = 3;
const salesAssistantId = 21;
const addressObservation = 'nuevo texto';
const ticketObservation = 'texto a modificar';
try {
const options = {transaction: tx};
ctx.req.accessToken.userId = salesAssistantId;
ctx.args = {
updateObservations: true,
incotermsFk: incotermsId,
provinceFk: provinceId,
customsAgentFk: customAgentOneId
};
await models.AddressObservation.create({
addressFk: address,
observationTypeFk: observationType,
description: addressObservation
}, options);
await models.TicketObservation.create({
ticketFk: ticket,
observationTypeFk: observationType,
description: ticketObservation
}, options);
await models.Client.updateAddress(ctx, client, address, options);
const updatedObservation = await models.TicketObservation.findOne({
where: {ticketFk: ticket, observationTypeFk: observationType}
}, options);
expect(updatedObservation.description).toEqual(addressObservation);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -80,6 +80,10 @@ module.exports = function(Self) {
{
arg: 'latitude',
type: 'any',
},
{
arg: 'updateObservations',
type: 'boolean'
}
],
returns: {
@ -135,6 +139,17 @@ module.exports = function(Self) {
delete args.ctx; // Remove unwanted properties
const updatedAddress = await address.updateAttributes(ctx.args, myOptions);
if (args.updateObservations) {
const ticket = await Self.rawSql(`
UPDATE ticketObservation to2
JOIN ticket t ON t.id = to2.ticketFk
JOIN address a ON a.id = t.addressFk
JOIN addressObservation ao ON ao.addressFk = a.id
SET to2.description = ao.description
WHERE ao.observationTypeFk = to2.observationTypeFk
AND a.id = ?
AND t.shipped >= util.VN_CURDATE()`, [addressId], myOptions);
}
return updatedAddress;
};

View File

@ -21,7 +21,7 @@ module.exports = Self => {
}
],
returns: {
type: ['object'],
type: 'object',
root: true
},
http: {
@ -41,23 +41,28 @@ module.exports = Self => {
switch (param) {
case 'search':
return {or: [
{'d.clientFk': value},
{'d.clientName': {like: `%${value}%`}}
{'c.id': value},
{'c.name': {like: `%${value}%`}}
]};
}
});
filter = mergeFilters(ctx.args.filter, {where});
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
filter = mergeFilters({where: {'d.created': date, 'd.amount': {gt: 0}}}, ctx.args.filter);
filter = mergeFilters(filter, {where});
const stmts = [];
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
const stmt = new ParameterizedSQL(
`SELECT *
FROM (
SELECT
DISTINCT c.id clientFk,
let stmt = new ParameterizedSQL(
`CREATE OR REPLACE TEMPORARY TABLE tmp.defaulters
WITH clientObservations AS
(SELECT clientFk,text, created, workerFk
FROM vn.clientObservation
GROUP BY clientFk
ORDER BY created DESC
)SELECT c.id clientFk,
c.name clientName,
c.salesPersonFk,
c.businessTypeFk = 'worker' isWorker,
@ -80,36 +85,43 @@ module.exports = Self => {
JOIN client c ON c.id = d.clientFk
JOIN country cn ON cn.id = c.countryFk
JOIN payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN clientObservation co ON co.clientFk = c.id
LEFT JOIN clientObservations co ON co.clientFk = c.id
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN account.user uw ON uw.id = co.workerFk
LEFT JOIN (
SELECT r1.started, r1.clientFk, r1.finished
SELECT r1.started, r1.clientFk, r1.finished
FROM recovery r1
JOIN (
SELECT MAX(started) AS maxStarted, clientFk
SELECT MAX(started) maxStarted, clientFk
FROM recovery
GROUP BY clientFk
) r2 ON r1.clientFk = r2.clientFk
AND r1.started = r2.maxStarted
WHERE r1.finished
GROUP BY r1.clientFk
) r ON r.clientFk = c.id
LEFT JOIN workerDepartment wd ON wd.workerFk = u.id
LEFT JOIN department dp ON dp.id = wd.departmentFk
WHERE
d.created = ?
AND d.amount > 0
ORDER BY co.created DESC) d`
, [date]);
LEFT JOIN department dp ON dp.id = wd.departmentFk`);
stmt.merge(conn.makeWhere(filter.where));
stmt.merge(`GROUP BY d.clientFk`);
stmts.push(stmt);
stmt = new ParameterizedSQL(`
SELECT SUM(amount) amount
FROM tmp.defaulters
`);
stmts.push(stmt);
stmt = new ParameterizedSQL(`
SELECT *
FROM tmp.defaulters
`);
stmt.merge(conn.makeOrderBy(filter.order));
stmt.merge(conn.makeLimit(filter));
const itemsIndex = stmts.push(stmt) - 1;
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions);
return itemsIndex === 0 ? result : result[itemsIndex];
return {defaulters: result[itemsIndex], amount: result[itemsIndex - 1][0].amount};
};
};

View File

@ -11,10 +11,10 @@ describe('defaulter filter()', () => {
const ctx = {req: {accessToken: {userId: authUserId}}, args: {filter: filter}};
const result = await models.Defaulter.filter(ctx, null, options);
const firstRow = result[0];
const firstRow = result.defaulters[0];
expect(firstRow.clientFk).toEqual(1101);
expect(result.length).toEqual(5);
expect(result.defaulters.length).toEqual(5);
await tx.rollback();
} catch (e) {
@ -31,7 +31,7 @@ describe('defaulter filter()', () => {
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}};
const result = await models.Defaulter.filter(ctx, null, options);
const firstRow = result[0];
const firstRow = result.defaulters[0];
expect(firstRow.clientFk).toEqual(1101);
@ -50,7 +50,7 @@ describe('defaulter filter()', () => {
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 'Petter Parker'}};
const result = await models.Defaulter.filter(ctx, null, options);
const firstRow = result[0];
const firstRow = result.defaulters[0];
expect(firstRow.clientName).toEqual('Petter Parker');
@ -60,4 +60,23 @@ describe('defaulter filter()', () => {
throw e;
}
});
it('should return the defaulter the sum of every defaulters', async() => {
const tx = await models.Defaulter.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: authUserId}}, args: {search: 1101}};
const {defaulters, amount} = await models.Defaulter.filter(ctx, null, options);
const total = defaulters.reduce((total, row) => total + row.amount, 0);
expect(total).toEqual(amount);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -139,5 +139,23 @@
},
"Xdiario": {
"dataSource": "vn"
},
"BusinessReasonEnd": {
"dataSource": "vn"
},
"OccupationCode": {
"dataSource": "vn"
},
"WorkerBusinessProfessionalCategory": {
"dataSource": "vn"
},
"CalendarType": {
"dataSource": "vn"
},
"WorkerBusinessType": {
"dataSource": "vn"
},
"PayrollCategory": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,17 @@
{
"name": "BusinessReasonEnd",
"base": "VnModel",
"options": {
"mysql": {
"table": "businessReasonEnd"
}
},
"properties": {
"id": {
"type": "number"
},
"reason": {
"type": "string"
}
}
}

View File

@ -13,6 +13,63 @@
},
"workcenterFk" : {
"type": "number"
},
"companyCodeFk" : {
"type": "string"
},
"started" : {
"type": "date"
},
"ended" : {
"type": "date"
},
"workerBusiness" : {
"type": "string"
},
"reasonEndFk" : {
"type": "number"
},
"payedHolidays" : {
"type": "number"
},
"occupationCodeFk" : {
"type": "string"
},
"workerFk" : {
"type": "number"
},
"notes" : {
"type": "string"
},
"departmentFk": {
"type": "string"
},
"workerBusinessProfessionalCategoryFk": {
"type": "number"
},
"calendarTypeFk": {
"type": "string"
},
"isHourlyLabor": {
"type": "boolean"
},
"rate": {
"type": "number"
},
"workerBusinessCategoryFk": {
"type": "number"
},
"workerBusinessTypeFk": {
"type": "number"
},
"amount": {
"type": "number"
},
"workerBusinessAgreementFk": {
"type": "number"
},
"basicSalary": {
"type": "number"
}
},
"relations": {
@ -25,6 +82,59 @@
"type": "belongsTo",
"model": "Department",
"foreignKey": "departmentFk"
},
"workCenter": {
"type": "belongsTo",
"model": "WorkCenter",
"foreignKey": "workcenterFk"
},
"companyCode": {
"type": "belongsTo",
"model": "Company",
"foreignKey": "companyCodeFk",
"primaryKey": "code"
},
"reasonEnd": {
"type": "belongsTo",
"model": "BusinessReasonEnd",
"foreignKey": "reasonEndFk"
},
"occupationCode": {
"type": "belongsTo",
"model": "OccupationCode",
"foreignKey":"occupationCodeFk",
"primaryKey": "code"
},
"payrollCategory": {
"type": "belongsTo",
"model": "PayrollCategory",
"foreignKey":"workerBusinessCategoryFk"
},
"workerBusinessProfessionalCategory": {
"type": "belongsTo",
"model": "WorkerBusinessProfessionalCategory",
"foreignKey": "workerBusinessProfessionalCategoryFk"
},
"calendarType": {
"type": "belongsTo",
"model": "CalendarType",
"foreignKey": "calendarTypeFk"
},
"workerBusinessCategory": {
"type": "belongsTo",
"model": "WorkerBusinessCategory",
"foreignKey": "workerBusinessCategoryFk"
},
"workerBusinessType": {
"type": "belongsTo",
"model": "WorkerBusinessType",
"foreignKey": "workerBusinessTypeFk"
},
"workerBusinessAgreement": {
"type": "belongsTo",
"model": "WorkerBusinessAgreement",
"foreignKey": "workerBusinessAgreementFk"
}
}
}

View File

@ -0,0 +1,21 @@
{
"name": "CalendarType",
"base": "VnModel",
"options": {
"mysql": {
"table": "calendarType"
}
},
"properties": {
"id": {
"type": "number"
},
"description": {
"type": "string"
},
"hoursWeek": {
"type": "number"
},
"isPartial": "number"
}
}

View File

@ -0,0 +1,18 @@
{
"name": "OccupationCode",
"base": "VnModel",
"options": {
"mysql": {
"table": "occupationCode"
}
},
"properties": {
"code": {
"type": "string",
"id": true
},
"name": {
"type": "string"
}
}
}

View File

@ -0,0 +1,20 @@
{
"name": "PayrollCategory",
"base": "VnModel",
"options": {
"mysql": {
"table": "payrollCategories"
}
},
"properties": {
"id": {
"type": "number"
},
"description": {
"type": "string"
},
"rate": {
"type": "number"
}
}
}

View File

@ -0,0 +1,20 @@
{
"name": "WorkerBusinessProfessionalCategory",
"base": "VnModel",
"options": {
"mysql": {
"table": "professionalCategory"
}
},
"properties": {
"id": {
"type": "number"
},
"description": {
"type": "string"
},
"code": {
"type": "string"
}
}
}

View File

@ -0,0 +1,29 @@
{
"name": "WorkerBusinessAgreement",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerBusinessAgreement"
}
},
"properties": {
"id": {
"type": "number"
},
"name": {
"type": "string"
},
"monthHolidays": {
"type": "number"
},
"yearsHours": {
"type": "number"
},
"started": {
"type": "date"
},
"ended": {
"type": "date"
}
}
}

View File

@ -0,0 +1,27 @@
{
"name": "WorkerBusinessType",
"base": "VnModel",
"options": {
"mysql": {
"table": "workerBusinessType"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"name": {
"type": "string"
},
"isFullTime": {
"type": "number"
},
"isPermanent": {
"type": "number"
},
"hasHolidayEntitlement": {
"type": "number"
}
}
}

View File

@ -1,200 +1,2 @@
<vn-crud-model
vn-id="model"
url="Defaulters/filter"
filter="::$ctrl.filter"
limit="20"
order="amount DESC"
data="$ctrl.defaulters"
on-data-change="$ctrl.reCheck()"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
placeholder="Search client"
info="Search client by id or name"
auto-state="false"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<div>
<div class="totalBox" style="text-align: center;">
<h6 translate>Total</h6>
<vn-label-value
label="Balance due"
value="{{$ctrl.balanceDueTotal | currency: 'EUR': 2}}">
</vn-label-value>
</div>
</div>
<div class="vn-pa-md">
<vn-button
disabled="$ctrl.checked.length == 0"
ng-click="notesDialog.show()"
name="notesDialog"
vn-tooltip="Add observation"
icon="icon-notes">
</vn-button>
</div>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th field="clientFk">
<span translate>Client</span>
</th>
<th field="isWorker">
<span translate>Es trabajador</span>
</th>
<th field="salesPersonFk">
<span translate>Comercial</span>
</th>
<th field="countryFk">
<span translate>Country</span>
</th>
<th field="payMethod"
vn-tooltip="Pay Method">
<span translate>P.Method</span>
</th>
<th
field="amount"
vn-tooltip="Balance due">
<span translate>Balance D.</span>
</th>
<th
field="workerFk"
vn-tooltip="Worker who made the last observation">
<span translate>Author</span>
</th>
<th field="observation" expand>
<span translate>Last observation</span>
</th>
<th
vn-tooltip="Last observation date"
field="created">
<span translate>L. O. Date</span>
</th>
<th
vn-tooltip="Credit insurance"
field="creditInsurance"
shrink>
<span translate>Credit I.</span>
</th>
<th field="defaulterSinced">
<span translate>From</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="defaulter in $ctrl.defaulters">
<td shrink>
<vn-check
ng-model="defaulter.checked"
on-change="$ctrl.saveChecked(defaulter.clientFk)"
vn-click-stop>
</vn-check>
</td>
<td title="{{::defaulter.clientName}}">
<span
vn-click-stop="clientDescriptor.show($event, defaulter.clientFk)"
title ="{{::defaulter.clientName}}"
class="link">
{{::defaulter.clientName}}
</span>
</td>
<td>
<vn-check
ng-model="defaulter.isWorker"
disabled="true">
</vn-check>
</td>
<td>
<span
title="{{::defaulter.salesPersonName}}"
vn-click-stop="workerDescriptor.show($event, defaulter.salesPersonFk)"
class="link">
{{::defaulter.salesPersonName | dashIfEmpty}}
</span>
</td>
<td>
{{::defaulter.country}}
</td>
<td>
{{::defaulter.payMethod}}
</td>
<td>{{::defaulter.amount | currency: 'EUR': 2}}</td>
<td>
<span
title="{{::defaulter.workerName}}"
vn-click-stop="workerDescriptor.show($event, defaulter.workerFk)"
class="link">
{{::defaulter.workerName | dashIfEmpty}}
</span>
</td>
<td expand>
<vn-textarea
vn-three
disabled="true"
ng-model="defaulter.observation">
</vn-textarea>
</td>
<td shrink-date>
<span class="chip {{::$ctrl.chipColor(defaulter.created)}}">
{{::defaulter.created | date: 'dd/MM/yyyy'}}
</span>
</td>
<td shrink>{{::defaulter.creditInsurance | currency: 'EUR': 2}}</td>
<td shrink-date>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-client-descriptor-popover
vn-id="client-descriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="dialog-summary-client">
<vn-client-summary
client="$ctrl.clientSelected">
</vn-client-summary>
</vn-popup>
<!-- Dialog of add notes button -->
<vn-dialog
vn-id="notesDialog"
on-accept="$ctrl.onResponse()">
<tpl-body>
<section class="SMSDialog">
<h5 class="vn-py-sm">{{$ctrl.$t('Add observation to all selected clients', {total: $ctrl.checked.length})}}</h5>
<vn-horizontal>
<vn-textarea vn-one
vn-id="message"
label="Message"
ng-model="$ctrl.defaulter.observation"
rows="3"
required="true"
rule>
</vn-textarea>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,199 +1,13 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.defaulter = {};
this.defaulters = [];
this.checkedDefaulers = [];
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'clientFk',
autocomplete: {
url: 'Clients',
showField: 'name',
valueField: 'id'
}
}, {
field: 'salesPersonFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
}, {
field: 'countryFk',
autocomplete: {
url: 'Countries',
showField: 'country',
valueField: 'id'
}
}, {
field: 'payMethodFk',
autocomplete: {
showField: 'name',
valueField: 'id'
}
},
{
field: 'workerFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
},
{
field: 'observation',
searchable: false
},
{
field: 'isWorker',
checkbox: true,
},
{
field: 'created',
datepicker: true
},
{
field: 'defaulterSinced',
datepicker: true
}
]
};
this.getBalanceDueTotal();
}
set defaulters(value) {
if (!value || !value.length) return;
this._defaulters = value;
}
get defaulters() {
return this._defaulters;
}
get checked() {
const clients = this.$.model.data || [];
const checkedLines = [];
for (let defaulter of clients) {
if (defaulter.checked)
checkedLines.push(defaulter);
}
return checkedLines;
}
saveChecked(clientId) {
this.checkedDefaulers = this.checkedDefaulers.includes(clientId) ?
this.checkedDefaulers.filter(id => id !== clientId) : [...this.checkedDefaulers, clientId];
}
reCheck() {
if (!this.$.model.data || !this.checkedDefaulers.length) return;
this.$.model.data.forEach(defaulter => {
defaulter.checked = this.checkedDefaulers.includes(defaulter.clientFk);
});
}
getBalanceDueTotal() {
this.$http.get('Defaulters/filter')
.then(res => {
if (!res.data) return 0;
this.balanceDueTotal = res.data.reduce(
(accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
}, 0);
});
}
chipColor(date) {
const day = 24 * 60 * 60 * 1000;
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const observationShipped = new Date(date);
observationShipped.setHours(0, 0, 0, 0);
const difference = today - observationShipped;
if (difference > (day * 20))
return 'alert';
if (difference > (day * 10))
return 'warning';
}
onResponse() {
if (!this.defaulter.observation)
throw new UserError(`The message can't be empty`);
const params = [];
for (let defaulter of this.checked) {
params.push({
text: this.defaulter.observation,
clientFk: defaulter.clientFk
});
}
this.$http.post(`ClientObservations`, params) .then(() => {
this.vnApp.showSuccess(this.$t('Observation saved!'));
this.sendMail();
this.$state.reload();
});
}
sendMail() {
const params = {
defaulters: this.checked,
observation: this.defaulter.observation,
};
this.$http.post(`Defaulters/observationEmail`, params);
}
exprBuilder(param, value) {
switch (param) {
case 'isWorker':
return {isWorker: value};
case 'creditInsurance':
case 'amount':
case 'clientFk':
case 'workerFk':
case 'countryFk':
case 'payMethod':
case 'salesPersonFk':
return {[`d.${param}`]: value};
case 'created':
return {'d.created': {
between: this.dateRange(value)}
};
case 'defaulterSinced':
return {'d.defaulterSinced': {
between: this.dateRange(value)}
};
}
}
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];
async $onInit() {
this.$state.go('customer.defaulter', {id: this.$params.id});
window.location.href = await this.vnApp.getUrl(`customer/defaulter`);
}
}

View File

@ -1,179 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('client defaulter', () => {
describe('Component vnClientDefaulter', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-defaulter></vn-client-defaulter>');
controller = $componentController('vnClientDefaulter', {$element});
controller.$.model = crudModel;
controller.$.model.data = [
{clientFk: 1101, amount: 125},
{clientFk: 1102, amount: 500},
{clientFk: 1103, amount: 250}
];
}));
describe('checked() getter', () => {
it('should return the checked lines', () => {
const data = controller.$.model.data;
data[1].checked = true;
data[2].checked = true;
const checkedRows = controller.checked;
const firstCheckedRow = checkedRows[0];
const secondCheckedRow = checkedRows[1];
expect(firstCheckedRow.clientFk).toEqual(1102);
expect(secondCheckedRow.clientFk).toEqual(1103);
});
});
describe('chipColor()', () => {
it('should return undefined when the date is the present', () => {
let today = Date.vnNew();
let result = controller.chipColor(today);
expect(result).toEqual(undefined);
});
it('should return warning when the date is 10 days in the past', () => {
let pastDate = Date.vnNew();
pastDate = pastDate.setDate(pastDate.getDate() - 11);
let result = controller.chipColor(pastDate);
expect(result).toEqual('warning');
});
it('should return alert when the date is 20 days in the past', () => {
let pastDate = Date.vnNew();
pastDate = pastDate.setDate(pastDate.getDate() - 21);
let result = controller.chipColor(pastDate);
expect(result).toEqual('alert');
});
});
describe('onResponse()', () => {
it('should return error for empty message', () => {
let error;
try {
controller.onResponse();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(`The message can't be empty`);
});
it('should return saved message', () => {
const data = controller.$.model.data;
data[1].checked = true;
controller.defaulter = {observation: 'My new observation'};
const params = [{text: controller.defaulter.observation, clientFk: data[1].clientFk}];
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('GET', `Defaulters/filter`).respond(200);
$httpBackend.expect('POST', `ClientObservations`, params).respond(200, params);
$httpBackend.expect('POST', `Defaulters/observationEmail`).respond(200);
controller.onResponse();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Observation saved!');
});
});
describe('exprBuilder()', () => {
it('should search by sales person', () => {
const expr = controller.exprBuilder('salesPersonFk', '5');
expect(expr).toEqual({'d.salesPersonFk': '5'});
});
it('should search by client', () => {
const expr = controller.exprBuilder('clientFk', '5');
expect(expr).toEqual({'d.clientFk': '5'});
});
});
describe('getBalanceDueTotal()', () => {
it('should return balance due total', () => {
const defaulters = controller.$.model.data;
$httpBackend.when('GET', `Defaulters/filter`).respond(defaulters);
controller.getBalanceDueTotal();
$httpBackend.flush();
expect(controller.balanceDueTotal).toEqual(875);
});
});
describe('dateRange()', () => {
it('should return two dates with the hours at the start and end of the given date', () => {
const now = Date.vnNew();
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('reCheck()', () => {
it(`should recheck buys`, () => {
controller.$.model.data = [
{checked: false, clientFk: 1},
{checked: false, clientFk: 2},
{checked: false, clientFk: 3},
{checked: false, clientFk: 4},
];
controller.checkedDefaulers = [1, 2];
controller.reCheck();
expect(controller.$.model.data[0].checked).toEqual(true);
expect(controller.$.model.data[1].checked).toEqual(true);
expect(controller.$.model.data[2].checked).toEqual(false);
expect(controller.$.model.data[3].checked).toEqual(false);
});
});
describe('saveChecked()', () => {
it(`should check buy`, () => {
const buyCheck = 3;
controller.checkedDefaulers = [1, 2];
controller.saveChecked(buyCheck);
expect(controller.checkedDefaulers[2]).toEqual(buyCheck);
});
it(`should uncheck buy`, () => {
const buyUncheck = 3;
controller.checkedDefaulers = [1, 2, 3];
controller.saveChecked(buyUncheck);
expect(controller.checkedDefaulers[2]).toEqual(undefined);
});
});
});
});

View File

@ -1,14 +0,0 @@
Add observation: Añadir observación
Add observation to all selected clients: Añadir observación a {{total}} cliente(s) seleccionado(s)
Balance D.: Saldo V.
Credit I.: Crédito A.
Last observation: Última observación
L. O. Date: Fecha Ú. O.
Last observation date: Fecha última observación
Search client: Buscar clientes
Worker who made the last observation: Trabajador que ha realizado la última observación
Email sended!: Email enviado!
Observation saved!: Observación añadida!
P.Method: F.Pago
Pay Method: Forma de Pago
Country: Pais

View File

@ -0,0 +1,43 @@
const models = require('vn-loopback/server/server').models;
describe('Transfer merchandise from one entry to the next day()', () => {
it('should Transfer buys not located', async() => {
const tx = await models.ItemShelving.beginTransaction({});
const options = {transaction: tx};
try {
const id = 3;
const item = 8;
const buy = 6;
const originalEntry = 4;
const ctx = {req: {accessToken: {userId: 48}}};
const currentItemShelving = await models.ItemShelving.findOne({where: {id}}, options);
await currentItemShelving.updateAttributes({itemFk: item, buyFk: buy}, options);
const {newEntryFk} = await models.Entry.transfer(ctx, originalEntry, options);
const originalEntrybuys = await models.Buy.find({where: {entryFk: originalEntry}}, options);
const newEntrybuys = await models.Buy.find({where: {entryFk: newEntryFk}}, options);
const itemShelvingsWithBuys = await models.Buy.find({
include: {
relation: 'itemShelving',
scope: {
fields: ['id'],
},
},
where: {entryFk: originalEntry},
}, options);
const hasItemShelving = await itemShelvingsWithBuys.filter(buy => buy.itemShelving().length);
expect(newEntrybuys.length).toEqual(originalEntrybuys.length - hasItemShelving.length);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,46 @@
module.exports = Self => {
Self.remoteMethodCtx('transfer', {
description: 'Transfer merchandise from one entry to the next day',
accessType: 'WRITE',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
http: {source: 'path'}
}
],
http: {
path: '/:id/transfer',
verb: 'POST'
},
returns: {
arg: 'newEntryFk',
type: 'number'
}
});
Self.transfer = async(ctx, id, options) => {
const myOptions = {userId: ctx.req.accessToken.userId};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
await Self.rawSql('CALL vn.entry_transfer(?, @vNewEntry)', [id], myOptions);
const [newEntryFk] = await Self.rawSql('SELECT @vNewEntry newEntryFk', null, myOptions);
if (tx) await tx.commit();
return newEntryFk;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -114,6 +114,11 @@
"type": "belongsTo",
"model": "Delivery",
"foreignKey": "deliveryFk"
}
},
"itemShelving": {
"type": "hasMany",
"model": "ItemShelving",
"foreignKey": "buyFk"
}
}
}

View File

@ -12,6 +12,7 @@ module.exports = Self => {
require('../methods/entry/addFromPackaging')(Self);
require('../methods/entry/addFromBuy')(Self);
require('../methods/entry/buyLabel')(Self);
require('../methods/entry/transfer')(Self);
require('../methods/entry/labelSupplier')(Self);
require('../methods/entry/buyLabelSupplier')(Self);

View File

@ -61,6 +61,11 @@
"type": "belongsTo",
"model": "Shelving",
"foreignKey": "shelvingFk"
}
},
"buy": {
"type": "belongsTo",
"model": "Buy",
"foreignKey": "buyFk"
}
}
}
}

View File

@ -10,4 +10,27 @@ module.exports = Self => {
return new UserError(`Tag value cannot be blank`);
return err;
});
Self.observe('before save', async ctx => {
let tagFk;
let value;
if (ctx.isNewInstance) {
tagFk = ctx.instance.tagFk;
value = ctx.instance.value;
} else {
tagFk = ctx.currentInstance.tagFk;
value = ctx.data.value;
}
const models = Self.app.models;
const validTag = await models.Tag.findById(tagFk);
if (validTag.validationRegex) {
const regexString = validTag.validationRegex.replace(/\\\\/g, '\\');
const validExpresion = new RegExp(regexString);
if (value && !validExpresion.test(value))
throw new UserError('The value must be a number or a range of numbers');
}
});
};

View File

@ -30,6 +30,9 @@
"mysql": {
"columnName": "isQuantitatif"
}
},
"validationRegex": {
"type": "string"
}
},
"acls": [

View File

@ -146,7 +146,7 @@ module.exports = Self => {
await fs.symlink(rootRelative, destinationRoot);
}
}
if (description) {
if (description && branch == 'master') {
let formatDesc;
const mainBranches = new Set(['master', 'test', 'dev']);
if (mainBranches.has(branch))

View File

@ -123,25 +123,13 @@ module.exports = Self => {
date.setHours(0, 0, 0, 0);
const args = ctx.args;
const myOptions = {};
let myTeamIds = [];
if (typeof options == 'object')
Object.assign(myOptions, options);
// Apply filter by team
const teamMembersId = [];
if (args.myTeam != null) {
const worker = await models.Worker.findById(userId, {
include: {
relation: 'collegues'
}
}, myOptions);
const collegues = worker.collegues() || [];
for (let collegue of collegues)
teamMembersId.push(collegue.collegueFk);
if (teamMembersId.length == 0)
teamMembersId.push(userId);
}
if (args.myTeam != null)
myTeamIds = await models.Worker.myTeam(userId);
if (ctx.args && args.to) {
const dateTo = args.to;
@ -163,9 +151,9 @@ module.exports = Self => {
case 'mine':
case 'myTeam':
if (value)
return {'c.salesPersonFk': {inq: teamMembersId}};
return {'c.salesPersonFk': {inq: myTeamIds}};
else
return {'c.salesPersonFk': {nin: teamMembersId}};
return {'c.salesPersonFk': {nin: myTeamIds}};
case 'id':
case 'clientFk':
param = `t.${param}`;

View File

@ -80,29 +80,15 @@ module.exports = Self => {
const conn = Self.dataSource.connector;
const myOptions = {};
const userId = ctx.req.accessToken.userId;
let myTeamIds = [];
if (typeof options == 'object')
Object.assign(myOptions, options);
const args = ctx.args;
// Apply filter by team
const teamMembersId = [];
if (args.myTeam != null) {
const worker = await models.Worker.findById(userId, {
include: {
relation: 'collegues'
}
}, myOptions);
const collegues = worker.collegues() || [];
for (let collegue of collegues)
teamMembersId.push(collegue.collegueFk);
if (teamMembersId.length == 0)
teamMembersId.push(userId);
}
if (args?.myTeam)
args.teamIds = teamIds;
if (args.myTeam != null)
myTeamIds = await models.Worker.myTeam(userId);
if (args?.to)
args.to.setHours(23, 59, 0, 0);
@ -133,9 +119,9 @@ module.exports = Self => {
return {'o.confirmed': value ? 1 : 0};
case 'myTeam':
if (value)
return {'c.salesPersonFk': {inq: teamMembersId}};
return {'c.salesPersonFk': {inq: myTeamIds}};
else
return {'c.salesPersonFk': {nin: teamMembersId}};
return {'c.salesPersonFk': {nin: myTeamIds}};
case 'showEmpty':
return {'o.total': {neq: value}};
case 'id':

View File

@ -1,109 +0,0 @@
const app = require('vn-loopback/server/server');
const models = require('vn-loopback/server/server').models;
describe('AgencyTerm filter()', () => {
const authUserId = 9;
const today = Date.vnNew();
today.setHours(2, 0, 0, 0);
it('should return all results matching the filter', async() => {
const tx = await models.AgencyTerm.beginTransaction({});
try {
const options = {transaction: tx};
const filter = {};
const ctx = {req: {accessToken: {userId: authUserId}}};
const agencyTerms = await models.AgencyTerm.filter(ctx, filter, options);
const firstAgencyTerm = agencyTerms[0];
expect(firstAgencyTerm.routeFk).toEqual(1);
expect(agencyTerms.length).toEqual(5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return results matching "search" searching by integer', async() => {
let ctx = {
args: {
search: 1,
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].routeFk).toEqual(1);
});
it('should return results matching "search" searching by string', async() => {
let ctx = {
args: {
search: 'Plants SL',
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(2);
});
it('should return results matching "from" and "to"', async() => {
const tx = await models.Buy.beginTransaction({});
const options = {transaction: tx};
try {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = Date.vnNew();
to.setHours(23, 59, 59, 999);
const ctx = {
args: {
from: from,
to: to
}
};
const results = await models.AgencyTerm.filter(ctx, options);
expect(results.length).toBe(5);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return results matching "agencyModeFk"', async() => {
let ctx = {
args: {
agencyModeFk: 1,
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].routeFk).toEqual(1);
});
it('should return results matching "agencyFk"', async() => {
let ctx = {
args: {
agencyFk: 2,
}
};
let result = await app.models.AgencyTerm.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].routeFk).toEqual(2);
});
});

View File

@ -87,6 +87,8 @@ module.exports = Self => {
Self.filter = async(ctx, filter) => {
let conn = Self.dataSource.connector;
const userId = ctx.req.accessToken.userId;
const models = Self.app.models;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
@ -110,6 +112,13 @@ module.exports = Self => {
filter = mergeFilters(filter, {where});
const worker = await models.Worker.findById(userId, {fields: ['isFreelance']});
const getMyRoute = await models.ACL.checkAccessAcl(ctx, 'Route', 'getRouteByAgency', 'WRITE');
if (userId && getMyRoute && worker.isFreelance) {
if (!filter.where) filter.where = {};
filter.where[`workerFk`] = userId;
}
let stmts = [];
let stmt;

View File

@ -8,14 +8,32 @@ describe('Route filter()', () => {
it('should return the routes matching "search"', async() => {
const ctx = {
args: {
search: 1,
search: 5,
},
req: {
accessToken: {
userId: 9
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].id).toEqual(5);
});
it('should return all results matching the filter', async() => {
const ctx = {
req: {
accessToken: {
userId: 133
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(1);
expect(result[0].id).toEqual(1);
expect(result.length).toEqual(3);
});
it('should return results matching "from" and "to"', async() => {
@ -32,9 +50,13 @@ describe('Route filter()', () => {
args: {
from: from,
to: to
},
req: {
accessToken: {
userId: 9
}
}
};
const results = await models.Route.filter(ctx, options);
expect(results.length).toBe(7);
@ -50,6 +72,11 @@ describe('Route filter()', () => {
const ctx = {
args: {
m3: 0.1,
},
req: {
accessToken: {
userId: 9
}
}
};
@ -62,6 +89,11 @@ describe('Route filter()', () => {
const ctx = {
args: {
description: 'third route',
},
req: {
accessToken: {
userId: 9
}
}
};
@ -74,18 +106,27 @@ describe('Route filter()', () => {
const ctx = {
args: {
workerFk: 56,
},
req: {
accessToken: {
userId: 9
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(5);
expect(result.length).toEqual(2);
});
it('should return the routes matching "warehouseFk"', async() => {
const ctx = {
args: {
warehouseFk: 1,
},
req: {
accessToken: {
userId: 9
}
}
};
@ -104,9 +145,13 @@ describe('Route filter()', () => {
const ctx = {
args: {
vehicleFk: 2,
},
req: {
accessToken: {
userId: 9
}
}
};
const result = await app.models.Route.filter(ctx);
expect(result.length).toEqual(1);
@ -116,6 +161,11 @@ describe('Route filter()', () => {
const ctx = {
args: {
agencyModeFk: 7,
},
req: {
accessToken: {
userId: 9
}
}
};

View File

@ -1,7 +1,16 @@
const models = require('vn-loopback/server/server').models;
const routeId = 1;
describe('route optimizePriority())', function() {
describe('route optimizePriority()', function() {
beforeEach(() => {
spyOn(models.OsrmConfig, 'optimize').and.returnValue(
Promise.resolve([
{addressId: 1, position: 0},
{addressId: 2, position: 1}
])
);
});
it('should execute without throwing errors', async function() {
const tx = await models.Route.beginTransaction({});
let error;
@ -20,9 +29,10 @@ describe('route optimizePriority())', function() {
}
expect(error).toBeUndefined();
expect(models.OsrmConfig.optimize).toHaveBeenCalled();
});
it('should execute with error', async function() {
it('should execute with error when all tickets have a route order', async function() {
let error;
try {
await models.Route.optimizePriority(routeId);

View File

@ -22,7 +22,7 @@ describe('route summary()', () => {
});
it(`should return a summary object containing it's worker`, async() => {
const result = await app.models.Route.summary(1);
const result = await app.models.Route.summary(2);
const worker = result.route.worker().user();
expect(worker.name).toEqual('delivery');

View File

@ -75,11 +75,6 @@
"type": "hasMany",
"model": "Ticket",
"foreignKey": "routeFk"
},
"zone": {
"type": "belongsTo",
"model": "Zone",
"foreignKey": "zoneFk"
}
}
}
}

View File

@ -0,0 +1,100 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const {buildFilter, mergeFilters} = require('vn-loopback/util/filter');
module.exports = Self => {
Self.remoteMethodCtx('receipts', {
description: 'Find all suppliers matched by the filter',
accessType: 'READ',
accepts: [
{
arg: 'filter',
type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}, {
arg: 'supplierId',
type: 'number',
description: 'The supplier id',
},
{
arg: 'companyId',
type: 'number',
description: 'The company id',
},
{
arg: 'currencyFk',
type: 'number',
description: 'The currency',
default: 1,
},
{
arg: 'bankFk',
type: 'number',
description: 'The bank',
default: 1,
},
{
arg: 'orderBy',
type: 'string',
description: 'The supplier fiscal id',
enum: ['issued', ' bookEntried', ' booked', ' dueDate'],
},
{
arg: 'isConciliated',
default: false,
type: 'boolean',
},
],
returns: {
type: ['object'],
root: true
},
http: {
path: `/receipts`,
verb: 'GET'
}
});
Self.receipts = async(ctx, filter, options) => {
const conn = Self.dataSource.connector;
const myOptions = {userId: ctx.req.accessToken.userId};
const args = ctx.args;
if (typeof options == 'object')
Object.assign(myOptions, options);
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'bankFk':
return {[param]: value};
}
});
let stmts = [];
stmts.push(new ParameterizedSQL('CALL vn.supplier_statementWithEntries(?,?,?,?,?,?)', [
args.supplierId,
args.currencyFk,
args.companyId,
args.orderBy ?? 'issued',
args.isConciliated ?? false,
false
]));
const stmt = new ParameterizedSQL(`
SELECT *
FROM tmp.supplierStatement`);
filter = mergeFilters(args.filter, {where});
stmt.merge(conn.makeSuffix(filter));
stmts.push(stmt);
stmts.push(`DROP TEMPORARY TABLE tmp.supplierStatement`);
const sql = ParameterizedSQL.join(stmts, ';');
const results = await conn.executeStmt(sql);
const resultsIndex = stmts.length - 2;
const result = results[resultsIndex];
return result;
};
};

View File

@ -8,6 +8,7 @@ module.exports = Self => {
require('../methods/supplier/updateFiscalData')(Self);
require('../methods/supplier/consumption')(Self);
require('../methods/supplier/freeAgencies')(Self);
require('../methods/supplier/receipts')(Self);
require('../methods/supplier/campaignMetricsPdf')(Self);
require('../methods/supplier/campaignMetricsEmail')(Self);
require('../methods/supplier/newSupplier')(Self);

View File

@ -15,4 +15,4 @@ columns:
priceFixed: precio fijado
isPriceFixed: tiene precio fijado
isAdded: añadida
total: total
total: importe

View File

@ -34,8 +34,8 @@ module.exports = Self => {
state.code == 'PICKER_DESIGNED' || state.code == 'PRINTED'
)
);
const isAllowed = isAllEditable || isSomeEditable || state.alertLevel == 0;
const alertLevel = await models.AlertLevel.findOne({where: {code: 'FREE'}}, myOptions);
const isAllowed = isAllEditable || isSomeEditable || state.alertLevel == alertLevel.id;
return isAllowed;
};
};

Some files were not shown because too many files have changed in this diff Show More