diff --git a/CHANGELOG.md b/CHANGELOG.md index c4eb7d29a..67ffe9f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# Version 25.00 - 2025-01-14 + +### Added 🆕 + +- feat: refs #7235 add serialType parameter to getInvoiceDate and implement corresponding tests by:jgallego +- feat: refs #7301 update lastEntriesFilter to include landedDate and enhance test cases (origin/7301-removeRedundantInventories) by:pablone +- feat: refs #7880 error code and translations by:ivanm +- feat: refs #7924 add isCustomInspectionRequired field to item and update related logic by:jgallego +- feat: refs #8167 update canBeInvoiced method to include active status check and improve test cases by:jgallego +- feat: refs #8167 update locale and improve invoicing logic with error handling by:jgallego +- feat: refs #8246 added relation for the front's new field by:Jon +- feat: refs #8266 added itemFk and needed fixtures by:jtubau +- feat: refs #8324 country unique by:Carlos Andrés + +### Changed 📦 + + +### Fixed 🛠️ + +- feat: refs #8266 added itemFk and needed fixtures by:jtubau +- fix: add isCustomInspectionRequired column to item table for customs inspection indication by:jgallego +- fix: canBeInvoiced only in makeInvoice by:alexm +- fix: hotFix getMondayWeekYear by:alexm +- fix: refs #6598 update ACL property assignment by:jorgep +- fix: refs #6861 refs#6861 addPrevOK by:sergiodt +- fix: refs #7301 remove debug console log and update test cases in lastEntriesFilter by:pablone +- fix: refs #7301 update SQL fixtures and improve lastEntriesFilter logic by:pablone + # Version 24.52 - 2024-01-07 ### Added 🆕 diff --git a/back/methods/chat/sendCheckingPresence.js b/back/methods/chat/sendCheckingPresence.js index 83e8da304..955ed0240 100644 --- a/back/methods/chat/sendCheckingPresence.js +++ b/back/methods/chat/sendCheckingPresence.js @@ -67,7 +67,6 @@ module.exports = Self => { INSERT INTO util.debug (variable, value) VALUES ('sendCheckingPresence_error', ?) `, [`User: ${userId}, recipient: ${recipientId}, message: ${message}, error: ${e}`]); - throw e; } }; }; diff --git a/db/dump/.dump/data.sql b/db/dump/.dump/data.sql index a2df34218..b95890b0d 100644 --- a/db/dump/.dump/data.sql +++ b/db/dump/.dump/data.sql @@ -4,7 +4,7 @@ USE `util`; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -INSERT INTO `version` VALUES ('vn-database','11385','72bf27f08d3ddf646ec0bb6594fc79cecd4b72f2','2025-01-07 07:46:33','11395'); +INSERT INTO `version` VALUES ('vn-database','11391','43edb1f82e88dcc44eedc8501b93c1fac66d71e9','2025-01-14 07:32:09','11407'); INSERT INTO `versionLog` VALUES ('vn-database','10107','00-firstScript.sql','jenkins@10.0.2.69','2022-04-23 10:53:53',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','10112','00-firstScript.sql','jenkins@10.0.2.69','2022-05-09 09:14:53',NULL,NULL); @@ -1078,6 +1078,7 @@ INSERT INTO `versionLog` VALUES ('vn-database','11315','00-firstScript.sql','jen INSERT INTO `versionLog` VALUES ('vn-database','11316','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2024-11-26 07:05:30',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11317','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2024-11-26 07:05:30',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11319','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2024-11-26 07:05:30',NULL,NULL); +INSERT INTO `versionLog` VALUES ('vn-database','11320','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2025-01-14 07:32:07',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11321','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2024-11-26 07:05:30',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11322','00-entryAcl.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2024-12-10 07:20:04',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11324','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2024-11-13 10:49:47',NULL,NULL); @@ -1139,6 +1140,9 @@ INSERT INTO `versionLog` VALUES ('vn-database','11379','00-firstScript.sql','jen INSERT INTO `versionLog` VALUES ('vn-database','11379','01-secScript.sql','jenkins@db-proxy1.servers.dc.verdnatura.es','2025-01-07 07:46:32',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11384','00-firstScript.sql','jenkins@db-proxy1.servers.dc.verdnatura.es','2025-01-07 07:46:32',NULL,NULL); INSERT INTO `versionLog` VALUES ('vn-database','11385','00-firstScript.sql','jenkins@db-proxy1.servers.dc.verdnatura.es','2025-01-07 07:46:33',NULL,NULL); +INSERT INTO `versionLog` VALUES ('vn-database','11390','00-firstScript.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2025-01-14 07:32:08',NULL,NULL); +INSERT INTO `versionLog` VALUES ('vn-database','11391','00-itemAlter.sql','jenkins@db-proxy2.servers.dc.verdnatura.es','2025-01-14 07:32:08',NULL,NULL); +INSERT INTO `versionLog` VALUES ('vn-database','11400','00-firstScript.sql','jenkins@db-proxy1.servers.dc.verdnatura.es','2025-01-09 09:55:24',NULL,NULL); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; @@ -1515,6 +1519,7 @@ INSERT INTO `roleInherit` VALUES (378,101,15,19294); INSERT INTO `roleInherit` VALUES (379,103,121,19294); INSERT INTO `roleInherit` VALUES (381,119,123,19295); INSERT INTO `roleInherit` VALUES (382,48,72,783); +INSERT INTO `roleInherit` VALUES (383,114,111,19295); INSERT INTO `userPassword` VALUES (1,7,1,0,2,1); @@ -2311,9 +2316,9 @@ INSERT INTO `ACL` VALUES (938,'Worker','__get__mail','READ','ALLOW','ROLE','hr', INSERT INTO `ACL` VALUES (939,'Machine','*','*','ALLOW','ROLE','productionBoss',10578); INSERT INTO `ACL` VALUES (940,'ItemTypeLog','find','READ','ALLOW','ROLE','employee',10578); INSERT INTO `ACL` VALUES (941,'Entry','buyLabel','READ','ALLOW','ROLE','employee',10578); -INSERT INTO `ACL` VALUES (942,'Cmr','filter','READ','ALLOW','ROLE','production',10578); -INSERT INTO `ACL` VALUES (943,'Cmr','downloadZip','READ','ALLOW','ROLE','production',10578); -INSERT INTO `ACL` VALUES (944,'Cmr','print','READ','ALLOW','ROLE','production',10578); +INSERT INTO `ACL` VALUES (942,'Cmr','filter','READ','ALLOW','ROLE','employee',19295); +INSERT INTO `ACL` VALUES (943,'Cmr','downloadZip','READ','ALLOW','ROLE','employee',19295); +INSERT INTO `ACL` VALUES (944,'Cmr','print','READ','ALLOW','ROLE','employee',19295); INSERT INTO `ACL` VALUES (945,'Collection','create','WRITE','ALLOW','ROLE','productionBoss',10578); INSERT INTO `ACL` VALUES (946,'Collection','upsert','WRITE','ALLOW','ROLE','productionBoss',10578); INSERT INTO `ACL` VALUES (947,'Collection','replaceById','WRITE','ALLOW','ROLE','productionBoss',10578); @@ -2327,7 +2332,6 @@ INSERT INTO `ACL` VALUES (954,'RouteComplement','find','READ','ALLOW','ROLE','de INSERT INTO `ACL` VALUES (955,'RouteComplement','create','WRITE','ALLOW','ROLE','delivery',10578); INSERT INTO `ACL` VALUES (956,'RouteComplement','deleteById','WRITE','ALLOW','ROLE','delivery',10578); INSERT INTO `ACL` VALUES (957,'SaleGroup','find','READ','ALLOW','ROLE','production',10578); -INSERT INTO `ACL` VALUES (958,'Worker','canCreateAbsenceInPast','WRITE','ALLOW','ROLE','hr',10578); INSERT INTO `ACL` VALUES (959,'WorkerRelative','updateAttributes','*','ALLOW','ROLE','hr',10578); INSERT INTO `ACL` VALUES (960,'WorkerRelative','crud','WRITE','ALLOW','ROLE','hr',10578); INSERT INTO `ACL` VALUES (961,'WorkerRelative','findById','*','ALLOW','ROLE','hr',10578); @@ -2383,6 +2387,8 @@ INSERT INTO `ACL` VALUES (1010,'InventoryConfig','find','READ','ALLOW','ROLE','b INSERT INTO `ACL` VALUES (1011,'SiiTypeInvoiceIn','find','READ','ALLOW','ROLE','salesPerson',10578); INSERT INTO `ACL` VALUES (1012,'OsrmConfig','optimize','READ','ALLOW','ROLE','employee',10578); INSERT INTO `ACL` VALUES (1013,'Route','optimizePriority','*','ALLOW','ROLE','employee',10578); +INSERT INTO `ACL` VALUES (1014,'Worker','canModifyAbsenceInPast','WRITE','ALLOW','ROLE','hr',10578); +INSERT INTO `ACL` VALUES (1015,'Worker','__get__sip','READ','ALLOW','ROLE','employee',19294); INSERT INTO `fieldAcl` VALUES (1,'Client','name','update','employee'); INSERT INTO `fieldAcl` VALUES (2,'Client','contact','update','employee'); @@ -2725,7 +2731,7 @@ INSERT INTO `department` VALUES (124,NULL,'CONTROL INTERNO',122,123,NULL,72,0,0, INSERT INTO `department` VALUES (125,'spainTeam3','EQUIPO ESPAÑA 3',59,60,1118,0,0,0,2,0,43,'/1/43/',NULL,1,NULL,0,0,0,0,NULL,NULL,NULL,NULL); INSERT INTO `department` VALUES (126,NULL,'PRESERVADO',29,30,NULL,0,0,0,2,0,37,'/1/37/',NULL,0,NULL,0,1,1,0,NULL,NULL,NULL,NULL); INSERT INTO `department` VALUES (128,NULL,'PALETIZADO',31,32,NULL,0,1,0,2,0,37,'/1/37/',NULL,0,NULL,0,0,0,0,NULL,NULL,NULL,'PALLETIZING'); -INSERT INTO `department` VALUES (130,NULL,'REVISION',33,34,NULL,0,1,0,2,0,37,'/1/37/',NULL,0,NULL,0,0,0,1,NULL,NULL,NULL,'ON_CHECKING'); +INSERT INTO `department` VALUES (130,'reviewers','REVISION',33,34,NULL,0,1,0,2,0,37,'/1/37/',NULL,0,NULL,0,0,0,1,NULL,NULL,NULL,'ON_CHECKING'); INSERT INTO `department` VALUES (131,'greenhouse','INVERNADERO',105,106,NULL,0,0,0,2,0,58,'/1/58/',NULL,0,NULL,0,1,0,0,NULL,NULL,NULL,NULL); INSERT INTO `department` VALUES (132,NULL,'EQUIPO DC',61,62,1731,0,0,0,2,0,43,'/1/43/','dc_equipo',1,'gestioncomercial@verdnatura.es',0,0,0,0,NULL,NULL,NULL,NULL); INSERT INTO `department` VALUES (133,'franceTeamManagement','EQUIPO GESTIÓN FRANCIA',63,64,9751,72,0,0,2,0,43,'/1/43/','fr_equipo',1,'gestionfrancia@verdnatura.es',0,0,0,0,NULL,NULL,'3300',NULL); @@ -2740,12 +2746,12 @@ INSERT INTO `department` VALUES (146,NULL,'VERDNACOLOMBIA',3,4,NULL,72,0,0,2,0,2 INSERT INTO `department` VALUES (147,'spainTeamAsia','EQUIPO ESPAÑA ASIA',71,72,40214,0,0,0,2,0,43,'/1/43/','esA_equipo',1,'esA@verdnatura.es',0,0,0,0,NULL,NULL,'5500',NULL); INSERT INTO `department` VALUES (148,'franceTeamCatchment','EQUIPO CAPTACIÓN FRANCIA',73,74,25178,0,0,0,2,0,43,'/1/43/',NULL,1,NULL,0,0,0,0,NULL,NULL,'6000',NULL); INSERT INTO `department` VALUES (149,'spainTeamCatchment','EQUIPO ESPAÑA CAPTACIÓN',75,76,1203,0,0,0,2,0,43,'/1/43/','es_captacion_equipo',1,'es_captacion@verdnatura.es',0,0,0,0,NULL,NULL,'5700',NULL); -INSERT INTO `department` VALUES (150,'spainTeamLevanteIslands','EQUIPO ESPAÑA LEVANTE',77,78,1118,0,0,0,2,0,43,'/1/43/','es_levante_equipo',1,'levanteislas.verdnatura@gmail.com',0,0,0,0,NULL,NULL,'5000',NULL); -INSERT INTO `department` VALUES (151,'spainTeamNorthwest','EQUIPO ESPAÑA NOROESTE',79,80,7102,0,0,0,2,0,43,'/1/43/','es_noroeste_equipo',1,'noroeste.verdnatura@gmail.com',0,0,0,0,NULL,NULL,'5300',NULL); -INSERT INTO `department` VALUES (152,'spainTeamNortheast','EQUIPO ESPAÑA NORESTE',81,82,1118,0,0,0,2,0,43,'/1/43/','es_noreste_equipo',1,'noreste.verdnatura@gmail.com',0,0,0,0,NULL,NULL,'5200',NULL); -INSERT INTO `department` VALUES (153,'spainTeamSouth','EQUIPO ESPAÑA SUR',83,84,36578,0,0,0,2,0,43,'/1/43/','es_sur_equipo',1,'sur.verdnatura@gmail.com',0,0,0,0,NULL,NULL,'5400',NULL); -INSERT INTO `department` VALUES (154,'spainTeamCenter','EQUIPO ESPAÑA CENTRO',85,86,4661,0,0,0,2,0,43,'/1/43/','es_centro_equipo',1,'centro.verdnatura@gmail.com',0,0,0,0,NULL,NULL,'5100',NULL); -INSERT INTO `department` VALUES (155,'spainTeamVip','EQUIPO ESPAÑA VIP',87,88,5432,0,0,0,2,0,43,'/1/43/','es_vip_equipo',1,'vip.verdnatura@gmail.com',0,0,0,0,NULL,NULL,'5600',NULL); +INSERT INTO `department` VALUES (150,'spainTeamLevanteIslands','EQUIPO ESPAÑA LEVANTE',77,78,1118,0,0,0,2,0,43,'/1/43/','es_levante_equipo',1,'es_levante@verdnatura.es',0,0,0,0,NULL,NULL,'5000',NULL); +INSERT INTO `department` VALUES (151,'spainTeamNorthwest','EQUIPO ESPAÑA NOROESTE',79,80,7102,0,0,0,2,0,43,'/1/43/','es_noroeste_equipo',1,'es_noroeste@verdnatura.es',0,0,0,0,NULL,NULL,'5300',NULL); +INSERT INTO `department` VALUES (152,'spainTeamNortheast','EQUIPO ESPAÑA NORESTE',81,82,1118,0,0,0,2,0,43,'/1/43/','es_noreste_equipo',1,'es_noreste@verdnatura.es',0,0,0,0,NULL,NULL,'5200',NULL); +INSERT INTO `department` VALUES (153,'spainTeamSouth','EQUIPO ESPAÑA SUR',83,84,36578,0,0,0,2,0,43,'/1/43/','es_sur_equipo',1,'es_sur@verdnatura.es',0,0,0,0,NULL,NULL,'5400',NULL); +INSERT INTO `department` VALUES (154,'spainTeamCenter','EQUIPO ESPAÑA CENTRO',85,86,4661,0,0,0,2,0,43,'/1/43/','es_centro_equipo',1,'es_centro@verdnatura.es',0,0,0,0,NULL,NULL,'5100',NULL); +INSERT INTO `department` VALUES (155,'spainTeamVip','EQUIPO ESPAÑA VIP',87,88,5432,0,0,0,2,0,43,'/1/43/','es_vip_equipo',1,'es_vip@verdnatura.es',0,0,0,0,NULL,NULL,'5600',NULL); INSERT INTO `docuware` VALUES (1,'deliveryNote','Albaranes cliente','find','find','N__ALBAR_N',NULL); INSERT INTO `docuware` VALUES (2,'deliveryNote','Albaranes cliente','store','Archivar','N__ALBAR_N',NULL); @@ -3046,6 +3052,7 @@ INSERT INTO `message` VALUES (20,'clientNotVerified','Incomplete tax data, pleas INSERT INTO `message` VALUES (21,'quantityLessThanMin','The quantity cannot be less than the minimum'); INSERT INTO `message` VALUES (22,'ORDER_ROW_UNAVAILABLE','The ordered quantity exceeds the available'); INSERT INTO `message` VALUES (23,'AMOUNT_NOT_MATCH_GROUPING','The quantity ordered does not match the grouping'); +INSERT INTO `message` VALUES (24,'orderLinesWithZero','There are empty lines. Please delete them'); INSERT INTO `metatag` VALUES (2,'title','Verdnatura Levante SL, mayorista de flores, plantas y complementos para floristería y decoración'); INSERT INTO `metatag` VALUES (3,'description','Verdnatura Levante SL, mayorista de flores, plantas y complementos para floristería y decoración. Envío a toda España, pedidos por internet o por teléfono.'); diff --git a/db/dump/.dump/privileges.sql b/db/dump/.dump/privileges.sql index 460256b56..598bfdf75 100644 --- a/db/dump/.dump/privileges.sql +++ b/db/dump/.dump/privileges.sql @@ -1494,6 +1494,10 @@ INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','travelThermograph',' INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','thermograph','guillermo@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select',''); INSERT IGNORE INTO `tables_priv` VALUES ('','vn2008','buyerSalesAssistant','Tickets','guillermo@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Update',''); INSERT IGNORE INTO `tables_priv` VALUES ('','vn','hr','sim','jenkins@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select,Insert,Update,Delete',''); +INSERT IGNORE INTO `tables_priv` VALUES ('','vn','employee','zoneGeo','guillermo@db-proxy2.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select',''); +INSERT IGNORE INTO `tables_priv` VALUES ('','vn','buyer','itemCampaign','guillermo@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select',''); +INSERT IGNORE INTO `tables_priv` VALUES ('','vn','grafana','itemCampaign','guillermo@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select',''); +INSERT IGNORE INTO `tables_priv` VALUES ('','vn','buyer','campaign','guillermo@db-proxy1.servers.dc.verdnatura.es','0000-00-00 00:00:00','Select',''); /*!40000 ALTER TABLE `tables_priv` ENABLE KEYS */; /*!40000 ALTER TABLE `columns_priv` DISABLE KEYS */; diff --git a/db/dump/.dump/structure.sql b/db/dump/.dump/structure.sql index e52ed2a51..58f1e7591 100644 --- a/db/dump/.dump/structure.sql +++ b/db/dump/.dump/structure.sql @@ -6249,19 +6249,27 @@ BEGIN * @param vDateFrom Fecha desde * @param vDateTo Fecha hasta */ - IF vDateFrom IS NULL THEN - SET vDateFrom = util.VN_CURDATE() - INTERVAL WEEKDAY(util.VN_CURDATE()) DAY; + DECLARE vDaysInYear INT; + SET vDaysInYear = DATEDIFF(util.lastDayOfYear(CURDATE()), util.firstDayOfYear(CURDATE())); + + SET vDateFrom = COALESCE(vDateFrom, util.VN_CURDATE()); + SET vDateTo = COALESCE(vDateTo, util.VN_CURDATE()); + + IF DATEDIFF(vDateTo, vDateFrom) > vDaysInYear THEN + CALL util.throw('The period cannot be longer than one year'); END IF; - IF vDateTo IS NULL THEN - SET vDateTo = vDateFrom + INTERVAL 6 DAY; - END IF; + -- Obtiene el primer día de la semana de esa fecha + SET vDateFrom = DATE_SUB(vDateFrom, INTERVAL ((WEEKDAY(vDateFrom) + 1) % 7) DAY); + + -- Obtiene el último día de la semana de esa fecha + SET vDateTo = DATE_ADD(vDateTo, INTERVAL (6 - ((WEEKDAY(vDateTo) + 1) % 7)) DAY); CALL cache.last_buy_refresh(FALSE); REPLACE bs.waste - SELECT YEAR(t.shipped), - WEEK(t.shipped, 4), + SELECT YEARWEEK(t.shipped, 6) DIV 100, + WEEK(t.shipped, 6), it.workerFk, it.id, s.itemFk, @@ -6307,9 +6315,9 @@ BEGIN JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = w.id JOIN vn.buy b ON b.id = lb.buy_id - WHERE t.shipped BETWEEN vDateFrom AND vDateTo + WHERE t.shipped BETWEEN vDateFrom AND util.dayEnd(vDateTo) AND w.isManaged - GROUP BY YEAR(t.shipped), WEEK(t.shipped, 4), i.id; + GROUP BY YEARWEEK(t.shipped, 6) DIV 100, WEEK(t.shipped, 6), i.id; END ;; DELIMITER ; /*!50003 SET sql_mode = @saved_sql_mode */ ; @@ -13807,7 +13815,7 @@ BEGIN ) INTO vHas0Amount; IF vHas0Amount THEN - CALL util.throw('Hay líneas vacías. Por favor, elimínelas'); + CALL util.throw('orderLinesWithZero'); END IF; START TRANSACTION; @@ -28922,6 +28930,7 @@ CREATE TABLE `country` ( `isSocialNameUnique` tinyint(1) NOT NULL DEFAULT 1, PRIMARY KEY (`id`), UNIQUE KEY `country_unique` (`code`), + UNIQUE KEY `country_unique_name` (`name`), KEY `currency_id_fk_idx` (`currencyFk`), KEY `country_Ix4` (`name`), KEY `continent_id_fk_idx` (`continentFk`), @@ -31971,6 +31980,7 @@ CREATE TABLE `item` ( `value12` varchar(50) DEFAULT NULL, `tag13` varchar(20) DEFAULT NULL, `value13` varchar(50) DEFAULT NULL, + `isCustomInspectionRequired` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Indicates if the item requires physical inspection at customs', PRIMARY KEY (`id`), UNIQUE KEY `item_supplyResponseFk_idx` (`supplyResponseFk`), KEY `Color` (`inkFk`), @@ -68661,10 +68671,11 @@ BEGIN TRUE, sc.userFk, s.id - FROM vn.sectorCollection sc - JOIN vn.sectorCollectionSaleGroup scsg ON scsg.sectorCollectionFk = sc.id - JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = scsg.saleGroupFk - JOIN vn.state s ON s.code = 'OK PREVIOUS' + FROM sectorCollection sc + JOIN sectorCollectionSaleGroup scsg ON scsg.sectorCollectionFk = sc.id + JOIN saleGroupDetail sgd ON sgd.saleGroupFk = scsg.saleGroupFk + JOIN state s ON s.code = 'OK PREVIOUS' + JOIN itemShelvingSale iss ON iss.saleFk = sgd.saleFk WHERE sc.id = vSectorCollectionFk; END ;; DELIMITER ; @@ -90882,4 +90893,4 @@ USE `vn2008`; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2025-01-07 6:51:38 +-- Dump completed on 2025-01-14 6:39:04 diff --git a/db/dump/.dump/triggers.sql b/db/dump/.dump/triggers.sql index 039dbb2a8..fb72e9899 100644 --- a/db/dump/.dump/triggers.sql +++ b/db/dump/.dump/triggers.sql @@ -11499,4 +11499,4 @@ USE `vn2008`; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2025-01-07 6:51:57 +-- Dump completed on 2025-01-14 6:39:25 diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index ff896b84d..590fe34b6 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -158,13 +158,13 @@ INSERT INTO `account`.`mailForward`(`account`, `forwardTo`) -INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`) +INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`, `hasToDownloadRate`) VALUES - (1, 'EUR', 'Euro', 1), - (2, 'USD', 'Dollar USA', 1.4), - (3, 'GBP', 'Libra', 1), - (4, 'JPY', 'Yen Japones', 1), - (5, 'CNY', 'Yuan Chino', 1.2); + (1, 'EUR', 'Euro', 1, FALSE), + (2, 'USD', 'Dollar USA', 1.4, TRUE), + (3, 'GBP', 'Libra', 1, TRUE), + (4, 'JPY', 'Yen Japones', 1, FALSE), + (5, 'CNY', 'Yuan Chino', 1.2, TRUE); INSERT INTO `vn`.`country`(`id`, `name`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`) VALUES @@ -694,22 +694,22 @@ INSERT INTO `vn`.`invoiceOutExpense`(`id`, `invoiceOutFk`, `amount`, `expenseFk` (6, 4, 8.07, 2000000000, util.VN_CURDATE()), (7, 5, 8.07, 2000000000, util.VN_CURDATE()); -INSERT INTO `vn`.`zone` (`id`, `name`, `hour`, `agencyModeFk`, `travelingDays`, `price`, `bonus`, `itemMaxSize`) - VALUES - (1, 'Zone pickup A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100), - (2, 'Zone pickup B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100), - (3, 'Zone 247 A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 7, 1, 2, 0, 100), - (4, 'Zone 247 B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 7, 1, 2, 0, 100), - (5, 'Zone expensive A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 8, 1, 1000, 0, 100), - (6, 'Zone expensive B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 8, 1, 1000, 0, 100), - (7, 'Zone refund', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 23, 0, 1, 0, 100), - (8, 'Zone others', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 10, 0, 1, 0, 100), - (9, 'Zone superMan', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 2, 0, 1, 0, 100), - (10, 'Zone teleportation', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 3, 0, 1, 0, 100), - (11, 'Zone pickup C', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100), - (12, 'Zone entanglement', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 4, 0, 1, 0, 100), - (13, 'Zone quantum break', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 5, 0, 1, 0, 100); - +INSERT INTO `vn`.`zone` + (`id`, `name`, `hour`, `agencyModeFk`, `travelingDays`, `price`, `bonus`, `itemMaxSize`, `priceOptimum`) +VALUES + (1, 'Zone pickup A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100, 1), + (2, 'Zone pickup B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100, 1), + (3, 'Zone 247 A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 7, 1, 2, 0, 100, 1), + (4, 'Zone 247 B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 7, 1, 2, 0, 100, 1), + (5, 'Zone expensive A', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 8, 1, 1000, 0, 100, 500), + (6, 'Zone expensive B', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 8, 1, 1000, 0, 100, 500), + (7, 'Zone refund', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 23, 0, 1, 0, 100, 0.5), + (8, 'Zone others', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 10, 0, 1, 0, 100, 0.5), + (9, 'Zone superMan', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 2, 0, 1, 0, 100, 0.5), + (10, 'Zone teleportation', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 3, 0, 1, 0, 100, 0.5), + (11, 'Zone pickup C', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 1, 0, 1, 0, 100, 0.5), + (12, 'Zone entanglement', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 4, 0, 1, 0, 100, 0.5), + (13, 'Zone quantum break', CONCAT(util.VN_CURDATE(), ' ', TIME('23:59')), 5, 0, 1, 0, 100, 0.5); INSERT INTO `vn`.`zoneWarehouse` (`id`, `zoneFk`, `warehouseFk`) VALUES diff --git a/db/routines/vn/events/client_setPackagesDiscountFactor.sql b/db/routines/vn/events/client_setPackagesDiscountFactor.sql new file mode 100644 index 000000000..a0dc33cac --- /dev/null +++ b/db/routines/vn/events/client_setPackagesDiscountFactor.sql @@ -0,0 +1,8 @@ +DELIMITER $$ +CREATE OR REPLACE DEFINER=`vn`@`localhost` EVENT `vn`.`client_setPackagesDiscountFactor` + ON SCHEDULE EVERY 1 DAY + STARTS '2024-10-18 03:00:00.000' + ON COMPLETION PRESERVE + ENABLE +DO CALL client_setPackagesDiscountFactor()$$ +DELIMITER ; diff --git a/db/routines/vn/procedures/catalog_componentCalculate.sql b/db/routines/vn/procedures/catalog_componentCalculate.sql index e29e13a8c..aaf2db408 100644 --- a/db/routines/vn/procedures/catalog_componentCalculate.sql +++ b/db/routines/vn/procedures/catalog_componentCalculate.sql @@ -231,7 +231,19 @@ BEGIN SELECT tcc.warehouseFK, tcc.itemFk, c2.id, - z.inflation * ROUND(ic.cm3delivery * (IFNULL(zo.price,5000) - IFNULL(zo.bonus,0)) / (1000 * vc.standardFlowerBox) , 4) cost + z.inflation + * ROUND( + ic.cm3delivery + * ( + ( + zo.priceOptimum + (( zo.price - zo.priceOptimum) * 2 * ( 1 - c.packagesDiscountFactor)) + ) + - IFNULL(zo.bonus, 0) + ) + / (1000 * vc.standardFlowerBox), + 4 + ) cost + FROM tmp.ticketComponentCalculate tcc JOIN item i ON i.id = tcc.itemFk JOIN tmp.zoneOption zo ON zo.zoneFk = vZoneFk @@ -239,6 +251,7 @@ BEGIN JOIN agencyMode am ON am.id = z.agencyModeFk JOIN vn.volumeConfig vc JOIN vn.component c2 ON c2.code = 'delivery' + JOIN `client` c on c.id = vClientFk LEFT JOIN itemCost ic ON ic.warehouseFk = tcc.warehouseFk AND ic.itemFk = tcc.itemFk HAVING cost <> 0; diff --git a/db/routines/vn/procedures/client_setPackagesDiscountFactor.sql b/db/routines/vn/procedures/client_setPackagesDiscountFactor.sql new file mode 100644 index 000000000..f6068ca37 --- /dev/null +++ b/db/routines/vn/procedures/client_setPackagesDiscountFactor.sql @@ -0,0 +1,25 @@ +DELIMITER $$ + +CREATE OR REPLACE DEFINER=`vn`@`localhost` +PROCEDURE `vn`.`client_setPackagesDiscountFactor`() +BEGIN + /** + * Set the discount factor for the packages of the clients. + */ + UPDATE client c + JOIN ( + SELECT t.clientFk, + LEAST(( + SUM(t.packages) / COUNT(DISTINCT DATE(t.shipped)) + ) / cc.packagesOptimum, 1) discountFactor + FROM ticket t + JOIN clientConfig cc ON TRUE + WHERE t.shipped > util.VN_CURDATE() - INTERVAL cc.monthsToCalcOptimumPrice MONTH + AND t.packages + GROUP BY t.clientFk + ) ca ON c.id = ca.clientFk + SET c.packagesDiscountFactor = ca.discountFactor; + +END$$ + +DELIMITER ; diff --git a/db/routines/vn/procedures/zone_getAddresses.sql b/db/routines/vn/procedures/zone_getAddresses.sql index 2e5982c82..9946b0b73 100644 --- a/db/routines/vn/procedures/zone_getAddresses.sql +++ b/db/routines/vn/procedures/zone_getAddresses.sql @@ -1,26 +1,27 @@ DELIMITER $$ CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`zone_getAddresses`( vSelf INT, - vShipped DATE, + vLanded DATE, vDepartmentFk INT ) BEGIN /** * Devuelve un listado de todos los clientes activos * con consignatarios a los que se les puede - * vender producto para esa zona. + * entregar producto para esa zona. * * @param vSelf Id de zona - * @param vShipped Fecha de envio - * @param vDepartmentFk Id de departamento + * @param vLanded Fecha de entrega + * @param vDepartmentFk Id de departamento | NULL para mostrar todos * @return Un select */ CALL zone_getPostalCode(vSelf); WITH clientWithTicket AS ( - SELECT clientFk + SELECT DISTINCT clientFk FROM vn.ticket - WHERE shipped BETWEEN vShipped AND util.dayEnd(vShipped) + WHERE landed BETWEEN vLanded AND util.dayEnd(vLanded) + AND NOT isDeleted ) SELECT c.id, c.name, @@ -30,7 +31,7 @@ BEGIN u.name username, aai.invoiced, cnb.lastShipped, - cwt.clientFk + IF(cwt.clientFk, TRUE, FALSE) hasTicket FROM vn.client c JOIN vn.worker w ON w.id = c.salesPersonFk JOIN vn.workerDepartment wd ON wd.workerFk = w.id @@ -50,7 +51,7 @@ BEGIN AND c.isActive AND ct.code = 'normal' AND bt.code <> 'worker' - AND (d.id = vDepartmentFk OR NOT vDepartmentFk) + AND (d.id = vDepartmentFk OR vDepartmentFk IS NULL) GROUP BY c.id; DROP TEMPORARY TABLE tmp.zoneNodes; diff --git a/db/routines/vn/procedures/zone_getOptionsForShipment.sql b/db/routines/vn/procedures/zone_getOptionsForShipment.sql index fa48b0b0f..17d1b3d11 100644 --- a/db/routines/vn/procedures/zone_getOptionsForShipment.sql +++ b/db/routines/vn/procedures/zone_getOptionsForShipment.sql @@ -9,7 +9,7 @@ BEGIN * @return tmp.zoneOption(zoneFk, hour, travelingDays, price, bonus, specificity) The computed options */ DECLARE vHour TIME DEFAULT TIME(util.VN_NOW()); - + DROP TEMPORARY TABLE IF EXISTS tLandings; CREATE TEMPORARY TABLE tLandings (INDEX (eventFk)) @@ -30,6 +30,7 @@ BEGIN TIME(IFNULL(e.`hour`, z.`hour`)) `hour`, l.travelingDays, IFNULL(e.price, z.price) price, + IFNULL(e.priceOptimum, z.priceOptimum) priceOptimum, IFNULL(e.bonus, z.bonus) bonus, l.landed, vShipped shipped diff --git a/db/versions/11398-orangeRose/00-zoneEventPriceOptimum.sql b/db/versions/11398-orangeRose/00-zoneEventPriceOptimum.sql new file mode 100644 index 000000000..0440714e4 --- /dev/null +++ b/db/versions/11398-orangeRose/00-zoneEventPriceOptimum.sql @@ -0,0 +1,5 @@ +ALTER TABLE `vn`.`zoneEvent` + ADD COLUMN `priceOptimum` DECIMAL(10,2) NULL COMMENT 'Precio mínimo que puede pagar un bulto' + AFTER `price`, + ADD CONSTRAINT `ck_zoneEvent_priceOptimum` + CHECK (priceOptimum <= price) diff --git a/db/versions/11398-orangeRose/00-zonePriceOptimum.sql b/db/versions/11398-orangeRose/00-zonePriceOptimum.sql new file mode 100644 index 000000000..82b2001cd --- /dev/null +++ b/db/versions/11398-orangeRose/00-zonePriceOptimum.sql @@ -0,0 +1,5 @@ +ALTER TABLE `vn`.`zone` + ADD COLUMN `priceOptimum` DECIMAL(10,2) NOT NULL COMMENT 'Precio mínimo que puede pagar un bulto' + AFTER `price`, + ADD CONSTRAINT `ck_zone_priceOptimum` + CHECK (priceOptimum <= price) diff --git a/db/versions/11398-orangeRose/01-zoneUpdate.sql b/db/versions/11398-orangeRose/01-zoneUpdate.sql new file mode 100644 index 000000000..042f2a92b --- /dev/null +++ b/db/versions/11398-orangeRose/01-zoneUpdate.sql @@ -0,0 +1,2 @@ +UPDATE `vn`.`zone` + SET `priceOptimum` = `price`; diff --git a/db/versions/11398-orangeRose/02-clientAlter.sql b/db/versions/11398-orangeRose/02-clientAlter.sql new file mode 100644 index 000000000..b5275a301 --- /dev/null +++ b/db/versions/11398-orangeRose/02-clientAlter.sql @@ -0,0 +1,3 @@ +ALTER TABLE `vn`.`client` + ADD COLUMN `packagesDiscountFactor` DECIMAL(4,3) NOT NULL DEFAULT 1.000 + COMMENT 'Porcentaje de ajuste entre el numero de bultos medio del cliente, y el número medio óptimo para las zonas en las que compra'; diff --git a/db/versions/11398-orangeRose/03-clientConfig.sql b/db/versions/11398-orangeRose/03-clientConfig.sql new file mode 100644 index 000000000..2869f26a2 --- /dev/null +++ b/db/versions/11398-orangeRose/03-clientConfig.sql @@ -0,0 +1,3 @@ +ALTER TABLE `vn`.`clientConfig` + ADD COLUMN `packagesOptimum` INT UNSIGNED NOT NULL DEFAULT 20 COMMENT 'Numero de bultos por cliente/dia para conseguir el precio optimo', + ADD COLUMN `monthsToCalcOptimumPrice` TINYINT UNSIGNED NOT NULL DEFAULT 3 COMMENT 'Número de meses a usar para el cálculo de client.packagesDiscountFactor'; diff --git a/db/versions/11405-blackMoss/00-entryAlter.sql b/db/versions/11405-blackMoss/00-entryAlter.sql new file mode 100644 index 000000000..3320b9dd3 --- /dev/null +++ b/db/versions/11405-blackMoss/00-entryAlter.sql @@ -0,0 +1,3 @@ +ALTER TABLE `vn`.`entry` + ADD COLUMN `initialTemperature` decimal(10,2) DEFAULT NULL COMMENT 'Temperatura de como lo recibimos del proveedor ej. en colombia', + ADD COLUMN `finalTemperature` decimal(10,2) DEFAULT NULL COMMENT 'Temperatura final de como llega a nuestras instalaciones'; diff --git a/db/versions/11406-bronzeMoss/00-currrencyAlter.sql b/db/versions/11406-bronzeMoss/00-currrencyAlter.sql new file mode 100644 index 000000000..86465545e --- /dev/null +++ b/db/versions/11406-bronzeMoss/00-currrencyAlter.sql @@ -0,0 +1,2 @@ +ALTER TABLE `vn`.`currency` +ADD COLUMN `hasToDownloadRate` TINYINT(1) NOT NULL DEFAULT 0 comment 'Si se guarda el tipo de cambio diariamente en referenceRate'; diff --git a/db/versions/11406-bronzeMoss/01-currrencyUpdate.sql b/db/versions/11406-bronzeMoss/01-currrencyUpdate.sql new file mode 100644 index 000000000..5e0882de2 --- /dev/null +++ b/db/versions/11406-bronzeMoss/01-currrencyUpdate.sql @@ -0,0 +1,3 @@ +UPDATE `vn`.`currency` + SET `hasToDownloadRate` = TRUE + WHERE `code` IN ('USD', 'CNY', 'GBP'); diff --git a/db/versions/11407-turquoiseTulip/00-firstScript.sql b/db/versions/11407-turquoiseTulip/00-firstScript.sql new file mode 100644 index 000000000..72d29061d --- /dev/null +++ b/db/versions/11407-turquoiseTulip/00-firstScript.sql @@ -0,0 +1,2 @@ +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('VnUser','adminUser','WRITE','ALLOW','ROLE','sysadmin'); \ No newline at end of file diff --git a/db/versions/11410-blackTulip/00-firstScript.sql b/db/versions/11410-blackTulip/00-firstScript.sql new file mode 100644 index 000000000..e300c4b7c --- /dev/null +++ b/db/versions/11410-blackTulip/00-firstScript.sql @@ -0,0 +1,2 @@ +RENAME TABLE bi.f_tvc TO bi.f_tvc__; +ALTER TABLE bi.f_tvc__ COMMENT='@deprecated 2025-01-15'; \ No newline at end of file diff --git a/db/versions/11415-chocolateTulip/00-firstScript.sql b/db/versions/11415-chocolateTulip/00-firstScript.sql new file mode 100644 index 000000000..2ed7ec9b5 --- /dev/null +++ b/db/versions/11415-chocolateTulip/00-firstScript.sql @@ -0,0 +1 @@ +CREATE INDEX ticket_landed_IDX USING BTREE ON vn.ticket (landed); diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js index d9689e31a..af1dc56bc 100644 --- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js +++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js @@ -238,25 +238,11 @@ describe('Ticket Edit sale path', () => { await page.waitToClick(selectors.globalItems.cancelButton); }); - it('should select the third sale and create a claim of it', async() => { - await page.accessToSearchResult('16'); - await page.accessToSection('ticket.card.sale'); - await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox); - await page.waitToClick(selectors.ticketSales.moreMenu); - await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); - await page.waitToClick(selectors.globalItems.acceptButton); - await page.waitForNavigation(); - }); - - it('should search for a ticket then access to the sales section', async() => { - await page.goBack(); - await page.goBack(); + it('should select the third sale and delete it', async() => { await page.loginAndModule('salesPerson', 'ticket'); await page.accessToSearchResult('16'); await page.accessToSection('ticket.card.sale'); - }); - it('should select the third sale and delete it', async() => { await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox); await page.waitToClick(selectors.ticketSales.deleteSaleButton); await page.waitToClick(selectors.globalItems.acceptButton); diff --git a/e2e/paths/05-ticket/06_basic_data_steps.spec.js b/e2e/paths/05-ticket/06_basic_data_steps.spec.js index 77f0e0459..0a3ae4edc 100644 --- a/e2e/paths/05-ticket/06_basic_data_steps.spec.js +++ b/e2e/paths/05-ticket/06_basic_data_steps.spec.js @@ -75,7 +75,7 @@ describe('Ticket Edit basic data path', () => { const result = await page .waitToGetProperty(selectors.ticketBasicData.stepTwoTotalPriceDif, 'innerText'); - expect(result).toContain('-€228.25'); + expect(result).toContain('-€111.75'); }); it(`should select a new reason for the changes made then click on finalize`, async() => { diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 818772e40..06428475f 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -247,9 +247,11 @@ "ticketLostExpedition": "The ticket [{{ticketId}}]({{{ticketUrl}}}) has the following lost expedition:{{ expeditionId }}", "The raid information is not correct": "The raid information is not correct", "Payment method is required": "Payment method is required", - "Sales already moved": "Sales already moved", - "Holidays to past days not available": "Holidays to past days not available", "Price cannot be blank": "Price cannot be blank", "There are tickets to be invoiced": "There are tickets to be invoiced", - "The address of the customer must have information about Incoterms and Customs Agent": "The address of the customer must have information about Incoterms and Customs Agent" -} + "The address of the customer must have information about Incoterms and Customs Agent": "The address of the customer must have information about Incoterms and Customs Agent", + "Sales already moved": "Sales already moved", + "Holidays to past days not available": "Holidays to past days not available", + "Incorrect delivery order alert on route": "Incorrect delivery order alert on route: {{ route }} zone: {{ zone }}", + "Ticket has been delivered out of order": "The ticket {{ticket}} {{{fullUrl}}} has been delivered out of order." +} \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 23a40a9fb..f79dad236 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -390,10 +390,6 @@ "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", - "No trips found because input coordinates are not connected": "No se encontraron rutas porque las coordenadas de entrada no están conectadas", - "This request is not supported": "Esta solicitud no es compatible", - "Invalid options or too many coordinates": "Opciones invalidas o demasiadas coordenadas", - "No address has coordinates": "Ninguna dirección tiene coordenadas", "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", diff --git a/loopback/locale/fr.json b/loopback/locale/fr.json index f49196a8f..d7d5b7710 100644 --- a/loopback/locale/fr.json +++ b/loopback/locale/fr.json @@ -362,9 +362,11 @@ "The invoices have been created but the PDFs could not be generated": "La facture a été émise mais le PDF n'a pas pu être généré", "It has been invoiced but the PDF of refund not be generated": "Il a été facturé mais le PDF de remboursement n'a pas été généré", "Cannot send mail": "Impossible d'envoyer le mail", - "Original invoice not found": "Facture originale introuvable", - "The quantity claimed cannot be greater than the quantity of the line": "Le montant réclamé ne peut pas être supérieur au montant de la ligne", - "You do not have permission to modify the booked field": "Vous n'avez pas la permission de modifier le champ comptabilisé", + "Original invoice not found": "Facture originale introuvable", + "The quantity claimed cannot be greater than the quantity of the line": "Le montant réclamé ne peut pas être supérieur au montant de la ligne", + "You do not have permission to modify the booked field": "Vous n'avez pas la permission de modifier le champ comptabilisé", "ticketLostExpedition": "Le ticket [{{ticketId}}]({{{ticketUrl}}}) a l'expédition perdue suivante : {{expeditionId}}", - "The web user's email already exists": "L'email de l'internaute existe déjà" -} + "The web user's email already exists": "L'email de l'internaute existe déjà", + "Incorrect delivery order alert on route": "Alerte de bon de livraison incorrect sur l'itinéraire: {{ route }} zone : {{ zone }}", + "Ticket has been delivered out of order": "Le ticket {{ticket}} {{{fullUrl}}} a été livré hors ordre." +} \ No newline at end of file diff --git a/loopback/locale/pt.json b/loopback/locale/pt.json index e2374d35f..d1ac2ef23 100644 --- a/loopback/locale/pt.json +++ b/loopback/locale/pt.json @@ -365,5 +365,7 @@ "Cannot send mail": "Não é possível enviar o email", "The quantity claimed cannot be greater than the quantity of the line": "O valor reclamado não pode ser superior ao valor da linha", "ticketLostExpedition": "O ticket [{{ticketId}}]({{{ticketUrl}}}) tem a seguinte expedição perdida: {{expeditionId}}", - "The web user's email already exists": "O e-mail do utilizador da web já existe." -} + "The web user's email already exists": "O e-mail do utilizador da web já existe.", + "Incorrect delivery order alert on route": "Alerta de ordem de entrega incorreta na rota: {{ route }} zona: {{ zone }}", + "Ticket has been delivered out of order": "O ticket {{ticket}} {{{fullUrl}}} foi entregue fora de ordem." +} \ No newline at end of file diff --git a/modules/client/back/methods/client/createAddress.js b/modules/client/back/methods/client/createAddress.js index 2709632cb..6bd4a26c1 100644 --- a/modules/client/back/methods/client/createAddress.js +++ b/modules/client/back/methods/client/createAddress.js @@ -52,6 +52,14 @@ module.exports = function(Self) { arg: 'customsAgentFk', type: 'number' }, + { + arg: 'longitude', + type: 'number' + }, + { + arg: 'latitude', + type: 'number' + }, { arg: 'isActive', type: 'boolean' diff --git a/modules/client/back/methods/defaulter/filter.js b/modules/client/back/methods/defaulter/filter.js index 5359ce4a7..cf8bd855a 100644 --- a/modules/client/back/methods/defaulter/filter.js +++ b/modules/client/back/methods/defaulter/filter.js @@ -94,7 +94,7 @@ module.exports = Self => { AND r1.started = r2.maxStarted ) r ON r.clientFk = c.id LEFT JOIN workerDepartment wd ON wd.workerFk = u.id - JOIN department dp ON dp.id = wd.departmentFk + LEFT JOIN department dp ON dp.id = wd.departmentFk WHERE d.created = ? AND d.amount > 0 diff --git a/modules/client/back/models/client-credit.json b/modules/client/back/models/client-credit.json index b92639b80..b57374dc3 100644 --- a/modules/client/back/models/client-credit.json +++ b/modules/client/back/models/client-credit.json @@ -19,6 +19,9 @@ }, "created": { "type": "date" + }, + "workerFk": { + "type": "number" } }, "relations": { @@ -33,4 +36,4 @@ "foreignKey": "workerFk" } } -} \ No newline at end of file +} diff --git a/modules/entry/back/methods/entry/filter.js b/modules/entry/back/methods/entry/filter.js index d7740dd4e..e5eae85fd 100644 --- a/modules/entry/back/methods/entry/filter.js +++ b/modules/entry/back/methods/entry/filter.js @@ -119,6 +119,16 @@ module.exports = Self => { arg: 'invoiceAmount', type: 'number', description: `The invoice amount` + }, + { + arg: 'initialTemperature', + type: 'number', + description: 'Initial temperature value' + }, + { + arg: 'finalTemperature', + type: 'number', + description: 'Final temperature value' } ], returns: { @@ -170,6 +180,10 @@ module.exports = Self => { case 'invoiceInFk': param = `e.${param}`; return {[param]: value}; + case 'initialTemperature': + return {'e.initialTemperature': {lte: value}}; + case 'finalTemperature': + return {'e.finalTemperature': {gte: value}}; } }); filter = mergeFilters(ctx.args.filter, {where}); @@ -204,6 +218,8 @@ module.exports = Self => { e.gestDocFk, e.invoiceInFk, e.invoiceAmount, + e.initialTemperature, + e.finalTemperature, t.landed, s.name supplierName, s.nickname supplierAlias, diff --git a/modules/entry/back/models/entry.json b/modules/entry/back/models/entry.json index 4a09c7d6a..1ff062119 100644 --- a/modules/entry/back/models/entry.json +++ b/modules/entry/back/models/entry.json @@ -68,6 +68,12 @@ }, "invoiceAmount": { "type": "number" + }, + "initialTemperature": { + "type": "number" + }, + "finalTemperature": { + "type": "number" } }, "relations": { diff --git a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js index 989b1d4a2..99ff4cd79 100644 --- a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js +++ b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js @@ -13,66 +13,114 @@ module.exports = Self => { } }); - Self.exchangeRateUpdate = async() => { - const response = await axios.get('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml'); - const xmlData = response.data; - - const doc = new DOMParser({errorHandler: {warning: () => {}}})?.parseFromString(xmlData, 'text/xml'); - const cubes = doc?.getElementsByTagName('Cube'); - if (!cubes || cubes.length === 0) - throw new UserError('No cubes found. Exiting the method.'); - + Self.exchangeRateUpdate = async(options = {}) => { const models = Self.app.models; + const myOptions = {}; + let tx; - const maxDateRecord = await models.ReferenceRate.findOne({order: 'dated DESC'}); + if (typeof options == 'object') + Object.assign(myOptions, options); - const maxDate = maxDateRecord?.dated ? new Date(maxDateRecord.dated) : null; + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const response = await axios.get('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml'); + const xmlData = response.data; + + const doc = new DOMParser({errorHandler: {warning: () => {}}}) + .parseFromString(xmlData, 'text/xml'); + const cubes = doc?.getElementsByTagName('Cube'); + if (!cubes || cubes.length === 0) + throw new UserError('No cubes found. Exiting the method.'); + + const currencies = await models.Currency.find({where: {hasToDownloadRate: true}}, myOptions); + const maxDateRecord = await models.ReferenceRate.findOne({order: 'dated DESC'}, myOptions); + const maxDate = maxDateRecord?.dated ? new Date(maxDateRecord.dated) : null; + let lastProcessedDate = maxDate; + + for (const cube of Array.from(cubes)) { + if (cube.nodeType === doc.ELEMENT_NODE && cube.attributes.getNamedItem('time')) { + const xmlDate = new Date(cube.getAttribute('time')); + const xmlDateWithoutTime = new Date( + xmlDate.getFullYear(), + xmlDate.getMonth(), + xmlDate.getDate() + ); + + if (!maxDate || xmlDateWithoutTime > maxDate) { + if (lastProcessedDate && xmlDateWithoutTime > lastProcessedDate) { + for (const currency of currencies) { + await fillMissingDates( + models, currency, lastProcessedDate, xmlDateWithoutTime, myOptions + ); + } + } + } - for (const cube of Array.from(cubes)) { - if (cube.nodeType === doc.ELEMENT_NODE && cube.attributes.getNamedItem('time')) { - const xmlDate = new Date(cube.getAttribute('time')); - const xmlDateWithoutTime = new Date(xmlDate.getFullYear(), xmlDate.getMonth(), xmlDate.getDate()); - if (!maxDate || maxDate < xmlDateWithoutTime) { for (const rateCube of Array.from(cube.childNodes)) { if (rateCube.nodeType === doc.ELEMENT_NODE) { const currencyCode = rateCube.getAttribute('currency'); const rate = rateCube.getAttribute('rate'); - if (['USD', 'CNY', 'GBP'].includes(currencyCode)) { - const currency = await models.Currency.findOne({where: {code: currencyCode}}); - if (!currency) throw new UserError(`Currency not found for code: ${currencyCode}`); + const currency = currencies.find(c => c.code === currencyCode); + if (currency) { const existingRate = await models.ReferenceRate.findOne({ - where: {currencyFk: currency.id, dated: xmlDate} - }); + where: {currencyFk: currency.id, dated: xmlDateWithoutTime} + }, myOptions); if (existingRate) { if (existingRate.value !== rate) - await existingRate.updateAttributes({value: rate}); + await existingRate.updateAttributes({value: rate}, myOptions); } else { await models.ReferenceRate.create({ currencyFk: currency.id, - dated: xmlDate, + dated: xmlDateWithoutTime, value: rate - }); - } - const monday = 1; - if (xmlDateWithoutTime.getDay() === monday) { - const saturday = new Date(xmlDateWithoutTime); - saturday.setDate(xmlDateWithoutTime.getDate() - 2); - const sunday = new Date(xmlDateWithoutTime); - sunday.setDate(xmlDateWithoutTime.getDate() - 1); - - for (const date of [saturday, sunday]) { - await models.ReferenceRate.upsertWithWhere( - {currencyFk: currency.id, dated: date}, - {currencyFk: currency.id, dated: date, value: rate} - ); - } + }, myOptions); } } } } + + lastProcessedDate = xmlDateWithoutTime; } } + + if (tx) await tx.commit(); + } catch (error) { + if (tx) await tx.rollback(); + throw error; } }; + + async function getLastValidRate(models, currencyId, date, myOptions) { + return models.ReferenceRate.findOne({ + where: {currencyFk: currencyId, dated: {lt: date}}, + order: 'dated DESC' + }, myOptions); + } + + async function fillMissingDates(models, currency, startDate, endDate, myOptions) { + const cursor = new Date(startDate); + cursor.setDate(cursor.getDate() + 1); + while (cursor < endDate) { + const existingRate = await models.ReferenceRate.findOne({ + where: {currencyFk: currency.id, dated: cursor} + }, myOptions); + + if (!existingRate) { + const lastValid = await getLastValidRate(models, currency.id, cursor, myOptions); + if (lastValid) { + await models.ReferenceRate.create({ + currencyFk: currency.id, + dated: new Date(cursor), + value: lastValid.value + }, myOptions); + } + } + cursor.setDate(cursor.getDate() + 1); + } + } }; diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js index 0fd7ea165..c3dcca5ae 100644 --- a/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js +++ b/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js @@ -1,52 +1,190 @@ describe('exchangeRateUpdate functionality', function() { const axios = require('axios'); const models = require('vn-loopback/server/server').models; + let tx; let options; - beforeEach(function() { - spyOn(axios, 'get').and.returnValue(Promise.resolve({ - data: ` - - - - - ` - })); + function formatYmd(d) { + const mm = (d.getMonth() + 1).toString().padStart(2, '0'); + const dd = d.getDate().toString().padStart(2, '0'); + return `${d.getFullYear()}-${mm}-${dd}`; + } + + afterEach(async() => { + await tx.rollback(); }); - it('should process XML data and update or create rates in the database', async function() { + beforeEach(async() => { + tx = await models.Sale.beginTransaction({}); + options = {transaction: tx}; + spyOn(axios, 'get').and.returnValue(Promise.resolve({data: ''})); + }); + + it('should process XML data and create rates', async function() { + const d1 = Date.vnNew(); + const d4 = Date.vnNew(); + d4.setDate(d4.getDate() + 1); + const xml = ` + + + + + + + + `; + axios.get.and.returnValue(Promise.resolve({data: xml})); spyOn(models.ReferenceRate, 'findOne').and.returnValue(Promise.resolve(null)); spyOn(models.ReferenceRate, 'create').and.returnValue(Promise.resolve()); + await models.InvoiceIn.exchangeRateUpdate(options); - await models.InvoiceIn.exchangeRateUpdate(); - - expect(models.ReferenceRate.create).toHaveBeenCalledTimes(2); + expect(models.ReferenceRate.create).toHaveBeenCalledTimes(3); }); - it('should not create or update rates when no XML data is available', async function() { + it('should handle no data', async function() { axios.get.and.returnValue(Promise.resolve({})); spyOn(models.ReferenceRate, 'create'); - - let thrownError = null; + let e; try { - await models.InvoiceIn.exchangeRateUpdate(); - } catch (error) { - thrownError = error; + await models.InvoiceIn.exchangeRateUpdate(options); + } catch (err) { + e = err; } - expect(thrownError.message).toBe('No cubes found. Exiting the method.'); + expect(e.message).toBe('No cubes found. Exiting the method.'); + expect(models.ReferenceRate.create).not.toHaveBeenCalled(); }); - it('should handle errors gracefully', async function() { + it('should handle errors', async function() { axios.get.and.returnValue(Promise.reject(new Error('Network error'))); - let error; - + let e; try { - await models.InvoiceIn.exchangeRateUpdate(); - } catch (e) { - error = e; + await models.InvoiceIn.exchangeRateUpdate(options); + } catch (err) { + e = err; } - expect(error).toBeDefined(); - expect(error.message).toBe('Network error'); + expect(e).toBeDefined(); + expect(e.message).toBe('Network error'); + }); + + it('should update existing rate', async function() { + const existingRate = await models.ReferenceRate.findOne({ + order: 'id DESC' + }, options); + + if (!existingRate) return fail('No ReferenceRate records in DB'); + + const currency = await models.Currency.findById(existingRate.currencyFk, null, options); + + const xml = ` + + + + `; + + axios.get.and.returnValue(Promise.resolve({data: xml})); + + await models.InvoiceIn.exchangeRateUpdate(options); + + const updatedRate = await models.ReferenceRate.findById(existingRate.id, null, options); + + expect(updatedRate.value).toBeCloseTo('2.22'); + }); + + it('should not update if same rate', async function() { + const existingRate = await models.ReferenceRate.findOne({order: 'id DESC'}, options); + if (!existingRate) return fail('No existing ReferenceRate in DB'); + + const currency = await models.Currency.findById(existingRate.currencyFk, null, options); + + const oldValue = existingRate.value; + const xml = ` + + + + `; + + axios.get.and.returnValue(Promise.resolve({data: xml})); + + await models.InvoiceIn.exchangeRateUpdate(options); + + const updatedRate = await models.ReferenceRate.findById(existingRate.id, null, options); + + expect(updatedRate.value).toBe(oldValue); + }); + + it('should backfill missing dates', async function() { + const lastRate = await models.ReferenceRate.findOne({order: 'dated DESC'}, options); + if (!lastRate) return fail('No existing ReferenceRate data in DB'); + + const currency = await models.Currency.findById(lastRate.currencyFk, null, options); + + const d1 = new Date(lastRate.dated); + d1.setDate(d1.getDate() + 1); + const d4 = new Date(lastRate.dated); + d4.setDate(d4.getDate() + 4); + + const xml = ` + + + + + + + `; + + axios.get.and.returnValue(Promise.resolve({data: xml})); + + const beforeCount = await models.ReferenceRate.count({}, options); + await models.InvoiceIn.exchangeRateUpdate(options); + const afterCount = await models.ReferenceRate.count({}, options); + + expect(afterCount - beforeCount).toBe(4); + }); + + it('should create entries for day1 and day2 from the feed, and not backfill day3', async function() { + const lastRate = await models.ReferenceRate.findOne({order: 'dated DESC'}, options); + if (!lastRate) return fail('No existing ReferenceRate data in DB'); + + const currency = await models.Currency.findById(lastRate.currencyFk, null, options); + if (!currency) return fail(`No currency for ID ${lastRate.currencyFk}`); + + const day1 = new Date(lastRate.dated); + day1.setDate(day1.getDate() + 1); + + const day2 = new Date(lastRate.dated); + day2.setDate(day2.getDate() + 2); + + const day3 = new Date(lastRate.dated); + day3.setDate(day3.getDate() + 3); + + const xml = ` + + + + + + + `; + + axios.get.and.returnValue(Promise.resolve({data: xml})); + + await models.InvoiceIn.exchangeRateUpdate(options); + + const day3Record = await models.ReferenceRate.findOne({ + where: {currencyFk: currency.id, dated: day3} + }, options); + + expect(day3Record).toBeNull(); + + const day1Record = await models.ReferenceRate.findOne({ + where: {currencyFk: currency.id, dated: day1} + }, options); + const day2Record = await models.ReferenceRate.findOne({ + where: {currencyFk: currency.id, dated: day2} + }, options); + + expect(day1Record.value).toBeCloseTo('1.1'); + expect(day2Record.value).toBeCloseTo('2.2'); }); }); diff --git a/modules/invoiceOut/back/methods/invoiceOut/clientsToInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/clientsToInvoice.js index f66221409..78fce348e 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/clientsToInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/clientsToInvoice.js @@ -74,7 +74,8 @@ module.exports = Self => { AND t.companyFk = ? AND NOT t.isDeleted GROUP BY IF(c.hasToInvoiceByAddress, a.id, c.id) - HAVING SUM(t.totalWithVat) > 0;`; + HAVING SUM(t.totalWithVat) > 0 + ORDER BY c.id`; const addresses = await Self.rawSql(query, [ minShipped, diff --git a/modules/supplier/back/methods/supplier/newSupplier.js b/modules/supplier/back/methods/supplier/newSupplier.js index 3cca4195f..eb941ed69 100644 --- a/modules/supplier/back/methods/supplier/newSupplier.js +++ b/modules/supplier/back/methods/supplier/newSupplier.js @@ -28,6 +28,7 @@ module.exports = Self => { delete args.ctx; if (!args.name) throw new UserError('The social name cannot be empty'); + if (args.name !== args.name.toUpperCase()) throw new UserError('Social name should be uppercase'); const data = {...args, ...{nickname: args.name}}; const supplier = await models.Supplier.create(data, myOptions); diff --git a/modules/ticket/back/methods/ticket/saveSign.js b/modules/ticket/back/methods/ticket/saveSign.js index ac2a7bc66..f99311c39 100644 --- a/modules/ticket/back/methods/ticket/saveSign.js +++ b/modules/ticket/back/methods/ticket/saveSign.js @@ -28,7 +28,6 @@ module.exports = Self => { verb: 'POST' } }); - Self.saveSign = async(ctx, tickets, location, signedTime, options) => { const models = Self.app.models; const myOptions = {userId: ctx.req.accessToken.userId}; @@ -111,6 +110,12 @@ module.exports = Self => { scope: { fields: ['id'] } + }, + { + relation: 'zone', + scope: { + fields: ['id', 'zoneFk,', 'name'] + } }] }, myOptions); @@ -151,6 +156,28 @@ module.exports = Self => { await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [ticketId, stateCode], myOptions); + if (stateCode == 'DELIVERED' && ticket.priority) { + const orderState = await models.State.findOne({ + where: {code: 'DELIVERED'}, + fields: ['id'] + }, myOptions); + + const ticketIncorrect = await Self.rawSql(` + SELECT t.id + FROM ticket t + JOIN ticketState ts ON ts.ticketFk = t.id + JOIN state s ON s.code = ts.code + WHERE t.routeFk = ? + AND s.\`order\` < ? + AND priority <(SELECT t.priority + FROM ticket t + WHERE t.id = ?)` + , [ticket.routeFk, orderState.id, ticket.id], myOptions); + + if (ticketIncorrect?.length > 0) + await sendMail(ctx, ticket.routeFk, ticket.id, ticket.zone().name); + } + if (ticket?.address()?.province()?.country()?.code != 'ES' && ticket.$cmrFk) { await models.Ticket.saveCmr(ctx, [ticketId], myOptions); externalTickets.push(ticketId); @@ -163,4 +190,25 @@ module.exports = Self => { } await models.Ticket.sendCmrEmail(ctx, externalTickets); }; + + async function sendMail(ctx, route, ticket, zoneName) { + const $t = ctx.req.__; + const url = await Self.app.models.Url.getUrl(); + const sendTo = 'repartos@verdnatura.es'; + const fullUrl = `${url}route/${route}/summary`; + const emailSubject = $t('Incorrect delivery order alert on route', { + route, + zone: zoneName + }); + const emailBody = $t('Ticket has been delivered out of order', { + ticket, + fullUrl + }); + + await Self.app.models.Mail.create({ + receiver: sendTo, + subject: emailSubject, + body: emailBody + }); + } }; diff --git a/modules/ticket/back/methods/ticket/specs/saveSign.spec.js b/modules/ticket/back/methods/ticket/specs/saveSign.spec.js index e93408973..3b426c2cf 100644 --- a/modules/ticket/back/methods/ticket/specs/saveSign.spec.js +++ b/modules/ticket/back/methods/ticket/specs/saveSign.spec.js @@ -1,12 +1,20 @@ const models = require('vn-loopback/server/server').models; +const LoopBackContext = require('loopback-context'); describe('Ticket saveSign()', () => { let ctx = {req: { getLocale: () => { return 'en'; }, + __: () => {}, accessToken: {userId: 9} - }}; + } + }; + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: ctx + }); + }); it(`should throw error if the ticket's alert level is lower than 2`, async() => { const tx = await models.TicketDms.beginTransaction({}); @@ -51,4 +59,46 @@ describe('Ticket saveSign()', () => { expect(ticketTrackingAfter.name).toBe('Entregado en parte'); }); + + it('should send an email to notify that the delivery order is not correct', async() => { + const tx = await models.Ticket.beginTransaction({}); + const ticketFk = 8; + const priority = 5; + const stateFk = 10; + const stateTicketFk = 2; + const expeditionFk = 11; + const expeditionStateFK = 2; + + let mailCountBefore; + let mailCountAfter; + spyOn(models.Dms, 'uploadFile').and.returnValue([{id: 1}]); + + const options = {transaction: tx}; + const tickets = [ticketFk]; + + const expedition = await models.Expedition.findById(expeditionFk, null, options); + expedition.updateAttribute('stateTypeFk', expeditionStateFK, options); + + const ticket = await models.Ticket.findById(ticketFk, null, options); + ticket.updateAttribute('priority', priority, options); + + const filter = {where: { + ticketFk: ticketFk, + stateFk: stateTicketFk} + }; + try { + const ticketTracking = await models.TicketTracking.findOne(filter, options); + ticketTracking.updateAttribute('stateFk', stateFk, options); + mailCountBefore = await models.Mail.count(options); + await models.Ticket.saveSign(ctx, tickets, null, null, options); + mailCountAfter = await models.Mail.count(options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + + expect(mailCountAfter).toBeGreaterThan(mailCountBefore); + }); }); diff --git a/modules/travel/back/methods/travel/filter.js b/modules/travel/back/methods/travel/filter.js index 30c1a45fa..837e30b30 100644 --- a/modules/travel/back/methods/travel/filter.js +++ b/modules/travel/back/methods/travel/filter.js @@ -1,4 +1,3 @@ - const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const buildFilter = require('vn-loopback/util/filter').buildFilter; const mergeFilters = require('vn-loopback/util/filter').mergeFilters; @@ -91,6 +90,11 @@ module.exports = Self => { arg: 'landed', type: 'date', description: 'The landed date' + }, + { + arg: 'awbFk', + type: 'number', + description: 'The awbFk id' } ], returns: { @@ -168,14 +172,17 @@ module.exports = Self => { t.totalEntries, t.isRaid, t.daysInForward, + t.awbFk, am.name agencyModeName, + a.code awbCode, win.name warehouseInName, wout.name warehouseOutName, cnt.code continent - FROM vn.travel t - JOIN vn.agencyMode am ON am.id = t.agencyModeFk - JOIN vn.warehouse win ON win.id = t.warehouseInFk - JOIN vn.warehouse wout ON wout.id = t.warehouseOutFk + FROM travel t + JOIN agencyMode am ON am.id = t.agencyModeFk + JOIN warehouse win ON win.id = t.warehouseInFk + JOIN warehouse wout ON wout.id = t.warehouseOutFk + LEFT JOIN awb a ON a.id = t.awbFk JOIN warehouse wo ON wo.id = t.warehouseOutFk JOIN country c ON c.id = wo.countryFk LEFT JOIN continent cnt ON cnt.id = c.continentFk) AS t` diff --git a/modules/travel/back/methods/travel/getEntries.js b/modules/travel/back/methods/travel/getEntries.js index 50088ccfa..2399f8bc4 100644 --- a/modules/travel/back/methods/travel/getEntries.js +++ b/modules/travel/back/methods/travel/getEntries.js @@ -41,7 +41,9 @@ module.exports = Self => { * b.stickers)/1000000) AS DECIMAL(10,2)) m3, TRUNCATE(SUM(b.stickers)/(COUNT( b.id) / COUNT( DISTINCT b.id)),0) hb, CAST(SUM(b.freightValue*b.quantity) AS DECIMAL(10,2)) freightValue, - CAST(SUM(b.packageValue*b.quantity) AS DECIMAL(10,2)) packageValue + CAST(SUM(b.packageValue*b.quantity) AS DECIMAL(10,2)) packageValue, + e.initialTemperature, + e.finalTemperature FROM vn.travel t LEFT JOIN vn.entry e ON t.id = e.travelFk LEFT JOIN vn.buy b ON b.entryFk = e.id diff --git a/modules/travel/back/models/currency.json b/modules/travel/back/models/currency.json index f3241fad1..427a18e31 100644 --- a/modules/travel/back/models/currency.json +++ b/modules/travel/back/models/currency.json @@ -20,6 +20,9 @@ }, "ratio": { "type": "number" + }, + "hasToDownloadRate": { + "type": "boolean" } }, "acls": [ diff --git a/modules/zone/back/models/zone-event.json b/modules/zone/back/models/zone-event.json index 366bdec9d..cf5045a8c 100644 --- a/modules/zone/back/models/zone-event.json +++ b/modules/zone/back/models/zone-event.json @@ -42,6 +42,9 @@ "price": { "type": "number" }, + "priceOptimum": { + "type": "number" + }, "bonus": { "type": "number" }, diff --git a/modules/zone/back/models/zone.json b/modules/zone/back/models/zone.json index 141b28750..4f963568f 100644 --- a/modules/zone/back/models/zone.json +++ b/modules/zone/back/models/zone.json @@ -28,6 +28,9 @@ "price": { "type": "number" }, + "priceOptimum": { + "type": "number" + }, "bonus": { "type": "number" }, diff --git a/package.json b/package.json index e4cbf1406..72f8e2d1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-back", - "version": "25.04.0", + "version": "25.06.0", "author": "Verdnatura Levante SL", "description": "Salix backend", "license": "GPL-3.0",