Merge branch 'master' into hotifx-loadDataDepartment
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Alex Moreno 2023-08-01 12:12:17 +00:00
commit b8780dfbbf
107 changed files with 2532 additions and 625 deletions

View File

@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2330.01] - 2023-07-27
### Added
- (Artículos -> Vista Previa) Añadido campo "Plástico reciclado"
- (Rutas -> Troncales) Nueva sección
- (Tickets -> Opciones) Opción establecer peso
- (Clientes -> SMS) Nueva sección
### Changed
- (General -> Iconos) Añadidos nuevos iconos
- (Clientes -> Razón social) Permite crear clientes con la misma razón social según el país
### Fixed
## [2328.01] - 2023-07-13 ## [2328.01] - 2023-07-13
### Added ### Added

View File

@ -22,6 +22,9 @@
}, },
"isUeeMember": { "isUeeMember": {
"type": "boolean" "type": "boolean"
},
"isSocialNameUnique": {
"type": "boolean"
} }
}, },
"relations": { "relations": {

View File

View File

@ -0,0 +1,15 @@
CREATE TABLE `vn`.`clientSms` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`clientFk` int(11) NOT NULL,
`smsFk` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `clientSms_FK` (`clientFk`),
KEY `clientSms_FK_1` (`smsFk`),
CONSTRAINT `clientSms_FK` FOREIGN KEY (`clientFk`) REFERENCES `client` (`id`) ON UPDATE CASCADE,
CONSTRAINT `clientSms_FK_1` FOREIGN KEY (`smsFk`) REFERENCES `sms` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('ClientSms', 'find', 'READ', 'ALLOW', 'ROLE', 'employee'),
('ClientSms', 'create', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('Vehicle','sorted','WRITE','ALLOW','employee');

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`item` ADD recycledPlastic INT NULL;
ALTER TABLE `vn`.`item` ADD nonRecycledPlastic INT NULL;

View File

@ -0,0 +1,64 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_inventory`(vParkingFromFk VARCHAR(8), vParkingToFk VARCHAR(8))
BEGIN
/**
* Devuelve un listado de ubicaciones a revisar
*
* @param vParkingFromFk Parking de partida, identificador de parking
* @param vParkingToFk Parking de llegada, identificador de parking
*/
DECLARE vSectorFk INT;
DECLARE vPickingOrderFrom INT;
DECLARE vPickingOrderTo INT;
SELECT p.sectorFk, p.pickingOrder INTO vSectorFk, vPickingOrderFrom
FROM parking p
WHERE p.code = vParkingFromFk COLLATE 'utf8mb3_general_ci';
SELECT p.pickingOrder INTO vPickingOrderTo
FROM parking p
WHERE p.code = vParkingToFk COLLATE 'utf8mb3_general_ci';
CALL visible_getMisfit(vSectorFk);
SELECT ish.id,
p.pickingOrder,
p.code parking,
ish.shelvingFk,
ish.itemFk,
i.longName,
ish.visible,
p.sectorFk,
it.workerFk buyer,
CONCAT('http:',ic.url, '/catalog/1600x900/',i.image) urlImage,
ish.isChecked,
CASE
WHEN s.notPrepared > sm.parked THEN 0
WHEN sm.visible > sm.parked THEN 1
ELSE 2
END priority
FROM itemShelving ish
JOIN item i ON i.id = ish.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN tmp.stockMisfit sm ON sm.itemFk = ish.itemFk
JOIN shelving sh ON sh.code = ish.shelvingFk
JOIN parking p ON p.id = sh.parkingFk
JOIN (SELECT s.itemFk, sum(s.quantity) notPrepared
FROM sale s
JOIN ticket t ON t.id = s.ticketFk
JOIN warehouse w ON w.id = t.warehouseFk
JOIN config c ON c.mainWarehouseFk = w.id
WHERE t.shipped BETWEEN util.VN_CURDATE()
AND util.dayEnd(util.VN_CURDATE())
AND s.isPicked = FALSE
GROUP BY s.itemFk) s ON s.itemFk = i.id
JOIN hedera.imageConfig ic
WHERE p.pickingOrder BETWEEN vPickingOrderFrom AND vPickingOrderTo
AND p.sectorFk = vSectorFk
ORDER BY p.pickingOrder;
END$$
DELIMITER ;

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`country`
ADD COLUMN `isSocialNameUnique` tinyint(1) NOT NULL DEFAULT 1;

View File

@ -0,0 +1,8 @@
ALTER TABLE `vn`.`roadmap` COMMENT='Troncales diarios que se contratan';
ALTER TABLE `vn`.`roadmap` ADD price decimal(10,2) NULL;
ALTER TABLE `vn`.`roadmap` ADD driverName varchar(45) NULL;
ALTER TABLE `vn`.`roadmap` ADD name varchar(45) NOT NULL;
ALTER TABLE `vn`.`roadmap` CHANGE name name varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL AFTER id;
ALTER TABLE `vn`.`roadmap` MODIFY COLUMN etd datetime NOT NULL;
ALTER TABLE `vn`.`expeditionTruck` COMMENT='Distintas paradas que hacen los trocales';

View File

@ -0,0 +1,6 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Roadmap', '*', '*', 'ALLOW', 'ROLE', 'palletizerBoss'),
('Roadmap', '*', '*', 'ALLOW', 'ROLE', 'productionBoss'),
('ExpeditionTruck', '*', '*', 'ALLOW', 'ROLE', 'palletizerBoss'),
('ExpeditionTruck', '*', '*', 'ALLOW', 'ROLE', 'productionBoss');

View File

@ -0,0 +1,7 @@
UPDATE `vn`.`ticket` t
JOIN `vn`.`ticketObservation` o ON o.ticketFk = t.id
SET t.weight = cast(REPLACE(o.description, ',', '.') as decimal(10,2))
WHERE o.observationTypeFk = 6;
DELETE FROM `vn`.`ticketObservation` WHERE observationTypeFk = 6;
DELETE FROM `vn`.`observationType` WHERE id = 6;

View File

@ -37,7 +37,7 @@ ALTER TABLE `vn`.`ticket` AUTO_INCREMENT = 1;
INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`) INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`)
VALUES VALUES
('DEFAULT_TOKEN', '1209600', util.VN_CURDATE(), 66); ('DEFAULT_TOKEN', '1209600', CURDATE(), 66);
INSERT INTO `salix`.`printConfig` (`id`, `itRecipient`, `incidencesEmail`) INSERT INTO `salix`.`printConfig` (`id`, `itRecipient`, `incidencesEmail`)
VALUES VALUES
@ -710,40 +710,40 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
(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), (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),
(7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5); (7, NULL, 57, util.VN_CURDATE(), 6, 8, 'seventh route', 0, 70, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 5);
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`) INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`, `weight`)
VALUES VALUES
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), (1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1),
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), (2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 2),
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)), (3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), NULL),
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)), (4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), NULL),
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)), (5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), NULL),
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)), (6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), NULL),
(7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(10, 1, 1, 5, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'Ingram Street', 2, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (10, 1, 1, 5, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'Ingram Street', 2, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(11, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (11, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(12, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (12, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(13, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (13, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(14, 1, 2, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1104, 'Malibu Point', 4, NULL, 0, 9, 5, 1, util.VN_CURDATE()), (14, 1, 2, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1104, 'Malibu Point', 4, NULL, 0, 9, 5, 1, util.VN_CURDATE(), NULL),
(15, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (15, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(16, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (16, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(17, 1, 7, 2, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE()), (17, 1, 7, 2, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1106, 'Many Places', 126, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL),
(18, 1, 4, 4, 4, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1108, 'Cerebro', 128, NULL, 0, 12, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +12 HOUR)), (18, 1, 4, 4, 4, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1108, 'Cerebro', 128, NULL, 0, 12, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +12 HOUR), NULL),
(19, 1, 5, 5, NULL, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 1, NULL, 5, 1, util.VN_CURDATE()), (19, 1, 5, 5, NULL, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 1, NULL, 5, 1, util.VN_CURDATE(), NULL),
(20, 1, 5, 5, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)), (20, 1, 5, 5, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Thailand', 129, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL),
(21, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Holland', 102, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)), (21, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Holland', 102, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL),
(22, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Japan', 103, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH)), (22, NULL, 5, 5, 5, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 1109, 'Somewhere in Japan', 103, NULL, 0, 13, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL +1 MONTH), NULL),
(23, NULL, 8, 1, 7, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'address 21', 121, NULL, 0, 5, 5, 1, util.VN_CURDATE()), (23, NULL, 8, 1, 7, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'address 21', 121, NULL, 0, 5, 5, 1, util.VN_CURDATE(), NULL),
(24 ,NULL, 8, 1, 7, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 5, 5, 1, util.VN_CURDATE()), (24 ,NULL, 8, 1, 7, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 5, 5, 1, util.VN_CURDATE(), NULL),
(25 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (25 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Bruce Wayne', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(26 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'An incredibly long alias for testing purposes', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (26 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'An incredibly long alias for testing purposes', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(27 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Wolverine', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (27 ,NULL, 8, 1, NULL, util.VN_CURDATE(), util.VN_CURDATE(), 1101, 'Wolverine', 1, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(28, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (28, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(29, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (29, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (30, 1, 8, 1, 1, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()), (31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL),
(32, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE()); (32, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL);
INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`)
VALUES VALUES
@ -2606,9 +2606,18 @@ INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
(3, 6, 5), (3, 6, 5),
(4, 7, 1); (4, 7, 1);
INSERT INTO `vn`.`expeditionTruck` (`id`, `eta`, `description`) INSERT INTO `vn`.`roadmap` (`id`, `name`, `tractorPlate`, `trailerPlate`, `phone`, `supplierFk`, `etd`, `observations`, `userFk`, `price`, `driverName`)
VALUES VALUES
(1, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +3 YEAR))), 'Best truck in fleet'); (1, 'val-algemesi', 'RE-001', 'PO-001', '111111111', 1, util.VN_NOW(), 'this is test observation', 1, 15, 'Batman'),
(2, 'alg-valencia', 'RE-002', 'PO-002', '111111111', 1, util.VN_NOW(), 'test observation', 1, 20, 'Robin'),
(3, 'alz-algemesi', 'RE-003', 'PO-003', '222222222', 2, DATE_ADD(util.VN_NOW(), INTERVAL 2 DAY), 'observations...', 2, 25, 'Driverman');
INSERT INTO `vn`.`expeditionTruck` (`id`, `roadmapFk`, `warehouseFk`, `eta`, `description`, `userFk`)
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),
(3, 1, 3, DATE_ADD(util.VN_NOW(), INTERVAL '1 4' DAY_HOUR), 'Third truck in fleet', 1),
(4, 2, 1, DATE_ADD(util.VN_NOW(), INTERVAL 3 DAY), 'Truck red', 1);
INSERT INTO `vn`.`expeditionPallet` (`id`, `truckFk`, `built`, `position`, `isPrint`) INSERT INTO `vn`.`expeditionPallet` (`id`, `truckFk`, `built`, `position`, `isPrint`)
VALUES VALUES
@ -2944,3 +2953,8 @@ INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `t
('E', 'Midgard', 1, 'CEE'), ('E', 'Midgard', 1, 'CEE'),
('R', 'Jotunheim', 1, 'NATIONAL'), ('R', 'Jotunheim', 1, 'NATIONAL'),
('W', 'Vanaheim', 1, 'WORLD'); ('W', 'Vanaheim', 1, 'WORLD');
INSERT INTO `hedera`.`imageConfig` (`id`, `maxSize`, `useXsendfile`, `url`)
VALUES
(1, 0, 0, 'marvel.com');

View File

@ -77831,7 +77831,7 @@ BEGIN
LEAVE cur1Loop; LEAVE cur1Loop;
END IF; END IF;
CALL zone_getLeaves2(vZoneFk, NULL, NULL); CALL zone_getLeaves(vZoneFk, NULL, NULL, TRUE);
myLoop: LOOP myLoop: LOOP
SET vGeoFk = NULL; SET vGeoFk = NULL;
@ -77844,7 +77844,7 @@ BEGIN
LEAVE myLoop; LEAVE myLoop;
END IF; END IF;
CALL zone_getLeaves2(vZoneFk, vGeoFk, NULL); CALL zone_getLeaves(vZoneFk, vGeoFk, NULL, TRUE);
UPDATE tmp.zoneNodes UPDATE tmp.zoneNodes
SET isChecked = TRUE SET isChecked = TRUE
WHERE geoFk = vGeoFk; WHERE geoFk = vGeoFk;
@ -78130,19 +78130,25 @@ DELIMITER ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ; /*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;; DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `zone_getLeaves`(vSelf INT, vParentFk INT, vSearch VARCHAR(255)) CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getLeaves`(
vSelf INT,
vParentFk INT,
vSearch VARCHAR(255),
vHasInsert BOOL
)
BEGIN BEGIN
/** /**
* Devuelve las ubicaciones incluidas en la ruta y que sean hijos de parentFk. * Devuelve las ubicaciones incluidas en la ruta y que sean hijos de parentFk.
* @param vSelf Id de la zona * @param vSelf Id de la zona
* @param vParentFk Id del geo a calcular * @param vParentFk Id del geo a calcular
* @param vSearch cadena a buscar * @param vSearch Cadena a buscar
* @param vHasInsert Indica si inserta en tmp.zoneNodes
* Optional @table tmp.zoneNodes(geoFk, name, parentFk, sons, isChecked, zoneFk)
*/ */
DECLARE vIsNumber BOOL; DECLARE vIsNumber BOOL;
DECLARE vIsSearch BOOL DEFAULT vSearch IS NOT NULL AND vSearch != ''; DECLARE vIsSearch BOOL DEFAULT vSearch IS NOT NULL AND vSearch <> '';
DROP TEMPORARY TABLE IF EXISTS tNodes; CREATE OR REPLACE TEMPORARY TABLE tNodes
CREATE TEMPORARY TABLE tNodes
(UNIQUE (id)) (UNIQUE (id))
ENGINE = MEMORY ENGINE = MEMORY
SELECT id SELECT id
@ -78167,18 +78173,15 @@ BEGIN
END IF; END IF;
IF vParentFk IS NULL THEN IF vParentFk IS NULL THEN
DROP TEMPORARY TABLE IF EXISTS tChilds; CREATE OR REPLACE TEMPORARY TABLE tChilds
CREATE TEMPORARY TABLE tChilds (INDEX(id))
ENGINE = MEMORY ENGINE = MEMORY
SELECT id SELECT id FROM tNodes;
FROM tNodes;
DROP TEMPORARY TABLE IF EXISTS tParents; CREATE OR REPLACE TEMPORARY TABLE tParents
CREATE TEMPORARY TABLE tParents (INDEX(id))
ENGINE = MEMORY ENGINE = MEMORY
SELECT id SELECT id FROM zoneGeo LIMIT 0;
FROM zoneGeo
LIMIT 0;
myLoop: LOOP myLoop: LOOP
DELETE FROM tParents; DELETE FROM tParents;
@ -78189,40 +78192,64 @@ BEGIN
WHERE g.parentFk IS NOT NULL; WHERE g.parentFk IS NOT NULL;
INSERT IGNORE INTO tNodes INSERT IGNORE INTO tNodes
SELECT id SELECT id FROM tParents;
FROM tParents;
IF ROW_COUNT() = 0 THEN IF NOT ROW_COUNT() THEN
LEAVE myLoop; LEAVE myLoop;
END IF; END IF;
DELETE FROM tChilds; DELETE FROM tChilds;
INSERT INTO tChilds INSERT INTO tChilds
SELECT id SELECT id FROM tParents;
FROM tParents;
END LOOP; END LOOP;
DROP TEMPORARY TABLE tChilds, tParents; DROP TEMPORARY TABLE tChilds, tParents;
END IF; END IF;
IF !vIsSearch THEN IF NOT vIsSearch THEN
INSERT IGNORE INTO tNodes INSERT IGNORE INTO tNodes
SELECT id SELECT id
FROM zoneGeo FROM zoneGeo
WHERE parentFk <=> vParentFk; WHERE parentFk <=> vParentFk;
END IF; END IF;
CREATE OR REPLACE TEMPORARY TABLE tZones
SELECT g.id, SELECT g.id,
g.name, g.name,
g.parentFk, g.parentFk,
g.sons, g.sons,
isIncluded selected NOT g.sons OR `type` = 'country' isChecked,
i.isIncluded selected,
g.`depth`,
vSelf
FROM zoneGeo g FROM zoneGeo g
JOIN tNodes n ON n.id = g.id JOIN tNodes n ON n.id = g.id
LEFT JOIN zoneIncluded i ON i.geoFk = g.id AND i.zoneFk = vSelf LEFT JOIN zoneIncluded i ON i.geoFk = g.id
ORDER BY `depth`, selected DESC, name; AND i.zoneFk = vSelf
ORDER BY g.`depth`, selected DESC, g.name;
DROP TEMPORARY TABLE tNodes; IF vHasInsert THEN
INSERT IGNORE INTO tmp.zoneNodes(geoFk, name, parentFk, sons, isChecked, zoneFk)
SELECT id,
name,
parentFk,
sons,
isChecked,
vSelf
FROM tZones
WHERE selected
OR (selected IS NULL AND vParentFk IS NOT NULL);
ELSE
SELECT id,
name,
parentFk,
sons,
selected
FROM tZones
ORDER BY `depth`, selected DESC, name;
END IF;
DROP TEMPORARY TABLE tNodes, tZones;
END ;; END ;;
DELIMITER ; DELIMITER ;
/*!50003 SET sql_mode = @saved_sql_mode */ ; /*!50003 SET sql_mode = @saved_sql_mode */ ;
@ -78540,7 +78567,7 @@ BEGIN
INDEX(geoFk)) INDEX(geoFk))
ENGINE = MEMORY; ENGINE = MEMORY;
CALL zone_getLeaves2(vSelf, NULL , NULL); CALL zone_getLeaves(vSelf, NULL , NULL, TRUE);
UPDATE tmp.zoneNodes zn UPDATE tmp.zoneNodes zn
SET isChecked = 0 SET isChecked = 0
@ -78553,7 +78580,7 @@ BEGIN
WHERE NOT isChecked WHERE NOT isChecked
LIMIT 1; LIMIT 1;
CALL zone_getLeaves2(vSelf, vGeoFk, NULL); CALL zone_getLeaves(vSelf, vGeoFk, NULL, TRUE);
UPDATE tmp.zoneNodes UPDATE tmp.zoneNodes
SET isChecked = TRUE SET isChecked = TRUE
WHERE geoFk = vGeoFk; WHERE geoFk = vGeoFk;

View File

@ -547,6 +547,7 @@ export default {
moreMenuMakeInvoice: '.vn-menu [name="makeInvoice"]', moreMenuMakeInvoice: '.vn-menu [name="makeInvoice"]',
moreMenuRegenerateInvoice: '.vn-menu [name="regenerateInvoice"]', moreMenuRegenerateInvoice: '.vn-menu [name="regenerateInvoice"]',
moreMenuChangeShippedHour: '.vn-menu [name="changeShipped"]', moreMenuChangeShippedHour: '.vn-menu [name="changeShipped"]',
moreMenuSMSOptions: '.vn-menu [name="smsOptions"]',
moreMenuPaymentSMS: '.vn-menu [name="sendPaymentSms"]', moreMenuPaymentSMS: '.vn-menu [name="sendPaymentSms"]',
moreMenuSendImportSms: '.vn-menu [name="sendImportSms"]', moreMenuSendImportSms: '.vn-menu [name="sendImportSms"]',
SMStext: 'textarea[name="message"]', SMStext: 'textarea[name="message"]',

View File

@ -124,6 +124,7 @@ describe('Ticket descriptor path', () => {
describe('SMS', () => { describe('SMS', () => {
it('should send the payment SMS using the descriptor menu', async() => { it('should send the payment SMS using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuSMSOptions);
await page.waitToClick(selectors.ticketDescriptor.moreMenuPaymentSMS); await page.waitToClick(selectors.ticketDescriptor.moreMenuPaymentSMS);
await page.waitForSelector(selectors.ticketDescriptor.SMStext); await page.waitForSelector(selectors.ticketDescriptor.SMStext);
await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 128); await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 128);
@ -134,8 +135,7 @@ describe('Ticket descriptor path', () => {
}); });
it('should send the import SMS using the descriptor menu', async() => { it('should send the import SMS using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu); await page.waitToClick(selectors.ticketDescriptor.moreMenuSMSOptions);
await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuSendImportSms); await page.waitToClick(selectors.ticketDescriptor.moreMenuSendImportSms);
await page.waitForSelector(selectors.ticketDescriptor.SMStext); await page.waitForSelector(selectors.ticketDescriptor.SMStext);
await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 144); await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 144);

View File

@ -23,6 +23,15 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-trailer:before {
content: "\e967";
}
.icon-grafana:before {
content: "\e965";
}
.icon-trolley:before {
content: "\e95c";
}
.icon-agency-term:before { .icon-agency-term:before {
content: "\e950"; content: "\e950";
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 173 KiB

View File

@ -95,7 +95,7 @@ module.exports = Self => {
// When claimState has been changed // When claimState has been changed
if (args.claimStateFk) { if (args.claimStateFk) {
const newState = await models.ClaimState.findById(args.claimStateFk, null, options); const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
if (newState.hasToNotify) { if (newState.hasToNotify) {
if (newState.code == 'incomplete') if (newState.code == 'incomplete')
notifyStateChange(ctx, salesPerson.id, claim, newState.code); notifyStateChange(ctx, salesPerson.id, claim, newState.code);

View File

@ -7,7 +7,7 @@ module.exports = Self => {
arg: 'id', arg: 'id',
type: 'number', type: 'number',
required: true, required: true,
description: 'The ticket id', description: 'The client id',
http: {source: 'path'} http: {source: 'path'}
}, },
{ {
@ -33,6 +33,12 @@ module.exports = Self => {
Self.sendSms = async(ctx, id, destination, message) => { Self.sendSms = async(ctx, id, destination, message) => {
const models = Self.app.models; const models = Self.app.models;
const sms = await models.Sms.send(ctx, destination, message); const sms = await models.Sms.send(ctx, destination, message);
await models.ClientSms.create({
clientFk: id,
smsFk: sms.id
});
return sms; return sms;
}; };
}; };

View File

@ -95,6 +95,9 @@
"ClientSample": { "ClientSample": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ClientSms": {
"dataSource": "vn"
},
"Sms": { "Sms": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,26 @@
{
"name": "ClientSms",
"base": "VnModel",
"options": {
"mysql": {
"table": "clientSms"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"clientFk": {
"type": "number"
}
},
"relations": {
"sms": {
"type": "belongsTo",
"model": "Sms",
"foreignKey": "smsFk"
}
}
}

View File

@ -41,7 +41,18 @@ module.exports = Self => {
}); });
async function socialNameIsUnique(err, done) { async function socialNameIsUnique(err, done) {
if (!this.countryFk)
return done();
const filter = { const filter = {
include: {
relation: 'country',
scope: {
fields: {
isSocialNameUnique: true,
},
},
},
where: { where: {
and: [ and: [
{socialName: this.socialName}, {socialName: this.socialName},
@ -50,9 +61,13 @@ module.exports = Self => {
] ]
} }
}; };
const client = await Self.app.models.Client.findOne(filter);
if (client) const client = await Self.app.models.Country.findById(this.countryFk, {fields: ['isSocialNameUnique']});
const existingClient = await Self.findOne(filter);
if (existingClient && (existingClient.country().isSocialNameUnique || client.isSocialNameUnique))
err(); err();
done(); done();
} }

View File

@ -181,6 +181,11 @@
"model": "Country", "model": "Country",
"foreignKey": "countryFk" "foreignKey": "countryFk"
}, },
"isSocialNameUnique": {
"type": "belongsTo",
"model": "Country",
"foreignKey": "countryFk"
},
"contactChannel": { "contactChannel": {
"type": "belongsTo", "type": "belongsTo",
"model": "ContactChannel", "model": "ContactChannel",

View File

@ -48,4 +48,5 @@ import './notification';
import './unpaid'; import './unpaid';
import './extended-list'; import './extended-list';
import './credit-management'; import './credit-management';
import './sms';

View File

@ -23,6 +23,7 @@
{"state": "client.card.recovery.index", "icon": "icon-recovery"}, {"state": "client.card.recovery.index", "icon": "icon-recovery"},
{"state": "client.card.webAccess", "icon": "cloud"}, {"state": "client.card.webAccess", "icon": "cloud"},
{"state": "client.card.log", "icon": "history"}, {"state": "client.card.log", "icon": "history"},
{"state": "client.card.sms", "icon": "sms"},
{ {
"description": "Credit management", "description": "Credit management",
"icon": "monetization_on", "icon": "monetization_on",
@ -373,6 +374,12 @@
"component": "vn-client-log", "component": "vn-client-log",
"description": "Log" "description": "Log"
}, },
{
"url" : "/sms",
"state": "client.card.sms",
"component": "vn-client-sms",
"description": "Sms"
},
{ {
"url": "/dms", "url": "/dms",
"state": "client.card.dms", "state": "client.card.dms",

View File

@ -0,0 +1,40 @@
<vn-crud-model
vn-id="model"
url="ClientSms"
link="{clientFk: $ctrl.$params.id}"
filter="::$ctrl.filter"
data="clientSmsList"
limit="20"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card class="vn-w-md">
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
<vn-th field="senderFk">Sender</vn-th>
<vn-th field="destination" number>Destination</vn-th>
<vn-th field="message">Message</vn-th>
<vn-th field="status">Status</vn-th>
<vn-th field="created" expand>Created</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="clientSms in clientSmsList">
<vn-td>
<span class="link" ng-click="workerDescriptor.show($event, clientSms.sms.senderFk)">
{{::clientSms.sms.sender.name}}
</span>
</vn-td>
<vn-td number expand>{{::clientSms.sms.destination}}</vn-td>
<vn-td>{{::clientSms.sms.message}}</vn-td>
<vn-td>{{::clientSms.sms.status}}</vn-td>
<vn-td shrink-datetime>{{::clientSms.sms.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>

View File

@ -0,0 +1,39 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
fields: ['id', 'smsFk'],
include: {
relation: 'sms',
scope: {
fields: [
'senderFk',
'sender',
'destination',
'message',
'statusCode',
'status',
'created'],
include: {
relation: 'sender',
scope: {
fields: ['name']
}
}
}
}
};
}
}
ngModule.vnComponent('vnClientSms', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -0,0 +1,2 @@
Sender: Remitente
Number sender: Número remitente

View File

@ -8,7 +8,7 @@ vn-client-summary .summary {
} }
vn-horizontal h4 .grafana:after { vn-horizontal h4 .grafana:after {
content: 'contact_support'; font-family: 'salixfont' !important;
font-size: 17px; content: "\e965";
} }
} }

View File

@ -9,8 +9,7 @@ vn-entry-buy-index vn-card {
} }
thead tr { thead tr {
border-left: 1px solid white; border: 1px solid white;;
border-right: 1px solid white;
} }
tbody tr:nth-child(1), tbody tr:nth-child(1),

View File

@ -0,0 +1,20 @@
name: invoice in
columns:
id: id
serialNumber: serial number
serial: serial
supplierFk: supplier
issued: issued
supplierRef: supplierRef
isBooked: is booked
currencyFk: currency
created: created
companyFk: company
docFk: document
booked: booked
operated: operated
bookEntried: book entried
isVatDeductible: is VAT deductible
withholdingSageFk: withholding
expenceFkDeductible: expence deductible
editorFk: editor

View File

@ -0,0 +1,20 @@
name: factura recibida
columns:
id: id
serialNumber: número de serie
serial: serie
supplierFk: proveedor
issued: fecha emisión
supplierRef: referéncia proveedor
isBooked: facturado
currencyFk: moneda
created: creado
companyFk: empresa
docFk: documento
booked: fecha contabilización
operated: fecha entrega
bookEntried: fecha asiento
isVatDeductible: impuesto deducible
withholdingSageFk: código de retención
expenceFkDeductible: gasto deducible
editorFk: editor

View File

@ -0,0 +1,9 @@
name: invoice in due day
columns:
id: id
invoiceInFk: invoice in
dueDated: due date
bankFk: bank
amount: amount
foreignValue : foreign amount
created: created

View File

@ -0,0 +1,9 @@
name: vencimientos factura recibida
columns:
id: id
invoiceInFk: factura
dueDated: fecha vto.
bankFk: banco
amount: importe
foreignValue : importe divisa
created: creado

View File

@ -0,0 +1,12 @@
name: invoice in tax
columns:
id: id
invoiceInFk: invoice in
taxCodeFk: tax
taxableBase: taxable base
expenceFk: expence
foreignValue: foreign amount
taxTypeSageFk: tax type
transactionTypeSageFk: transaction type
created: created
editorFk: editor

View File

@ -0,0 +1,12 @@
name: factura recibida impuesto
columns:
id: id
invoiceInFk: factura recibida
taxCodeFk: código IVA
taxableBase: base imponible
expenceFk: código gasto
foreignValue: importe divisa
taxTypeSageFk: código impuesto
transactionTypeSageFk: código transacción
created: creado
editorFk: editor

View File

@ -0,0 +1,37 @@
module.exports = Self => {
Self.remoteMethod('getInventory', {
description: 'Get list of itemShelving to review between two parking code',
accessType: 'WRITE',
accepts: [{
arg: 'parkingFrom',
type: 'string',
required: true,
description: 'Parking code from'
},
{
arg: 'parkingTo',
type: 'string',
required: true,
description: 'Parking code to'
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/getInventory`,
verb: 'POST'
}
});
Self.getInventory = async(parkingFrom, parkingTo, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const [result] = await Self.rawSql(`CALL vn.itemShelving_inventory(?, ?)`, [parkingFrom, parkingTo], myOptions);
return result;
};
};

View File

@ -0,0 +1,24 @@
const models = require('vn-loopback/server/server').models;
describe('itemShelving getInventory()', () => {
it('should return a list of itemShelvings', async() => {
const tx = await models.ItemShelving.beginTransaction({});
let response;
try {
const options = {transaction: tx};
await models.ItemShelving.rawSql(`
UPDATE vn.config
SET mainWarehouseFk=1
WHERE id=1
`, null, options);
response = await models.ItemShelving.getInventory('100-01', 'LR-02-3', options);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
expect(response.length).toEqual(2);
});
});

View File

@ -164,6 +164,8 @@ module.exports = Self => {
i.stemMultiplier, i.stemMultiplier,
i.typeFk, i.typeFk,
i.isFloramondo, i.isFloramondo,
i.recycledPlastic,
i.nonRecycledPlastic,
pr.name AS producer, pr.name AS producer,
it.name AS typeName, it.name AS typeName,
it.workerFk AS buyerFk, it.workerFk AS buyerFk,

View File

@ -1,3 +1,4 @@
module.exports = Self => { module.exports = Self => {
require('../methods/item-shelving/deleteItemShelvings')(Self); require('../methods/item-shelving/deleteItemShelvings')(Self);
require('../methods/item-shelving/getInventory')(Self);
}; };

View File

@ -23,6 +23,12 @@
}, },
"isChecked": { "isChecked": {
"type": "boolean" "type": "boolean"
},
"visible": {
"type": "number"
},
"userFk": {
"type": "number"
} }
}, },
"relations": { "relations": {

View File

@ -125,6 +125,12 @@
"minPrice": { "minPrice": {
"type": "number" "type": "number"
}, },
"recycledPlastic": {
"type": "number"
},
"nonRecycledPlastic": {
"type": "number"
},
"packingOut": { "packingOut": {
"type": "number" "type": "number"
}, },

View File

@ -82,6 +82,8 @@
vn-name="expence" vn-name="expence"
initial-data="$ctrl.item.expense"> initial-data="$ctrl.item.expense">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete <vn-autocomplete
data="originsData" data="originsData"
label="Origin" label="Origin"
@ -91,21 +93,6 @@
vn-name="origin" vn-name="origin"
initial-data="$ctrl.item.origin"> initial-data="$ctrl.item.origin">
</vn-autocomplete> </vn-autocomplete>
<vn-textfield
label="Reference"
ng-model="$ctrl.item.comment"
vn-name="comment"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="Relevancy"
ng-model="$ctrl.item.relevancy"
vn-name="relevancy"
rule>
</vn-input-number>
<vn-input-number <vn-input-number
min="0" min="0"
label="Size" label="Size"
@ -113,6 +100,21 @@
vn-name="size" vn-name="size"
rule> rule>
</vn-input-number> </vn-input-number>
<vn-textfield
label="Reference"
ng-model="$ctrl.item.comment"
vn-name="comment"
rule>
</vn-textfield>
<vn-input-number
min="0"
label="Relevancy"
ng-model="$ctrl.item.relevancy"
vn-name="relevancy"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number <vn-input-number
min="0" min="0"
label="stems" label="stems"
@ -126,22 +128,6 @@
ng-model="$ctrl.item.stemMultiplier" ng-model="$ctrl.item.stemMultiplier"
vn-name="stemMultiplier"> vn-name="stemMultiplier">
</vn-input-number> </vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="Weight/Piece"
ng-model="$ctrl.item.weightByPiece"
vn-name="weightByPiece"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Units/Box"
ng-model="$ctrl.item.packingOut"
vn-name="packingOut"
rule>
</vn-input-number>
<vn-autocomplete <vn-autocomplete
label="Generic" label="Generic"
url="Items/withName" url="Items/withName"
@ -167,6 +153,36 @@
</append> </append>
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="Weight/Piece"
ng-model="$ctrl.item.weightByPiece"
vn-name="weightByPiece"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Units/Box"
ng-model="$ctrl.item.packingOut"
vn-name="packingOut"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Recycled Plastic"
ng-model="$ctrl.item.recycledPlastic"
vn-name="recycledPlastic"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Non recycled plastic"
ng-model="$ctrl.item.nonRecycledPlastic"
vn-name="nonRecycledPlastic"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-textarea <vn-textarea
label="Description" label="Description"

View File

@ -14,3 +14,5 @@ Multiplier: Multiplicador
Generic: Genérico Generic: Genérico
This item does need a photo: Este artículo necesita una foto This item does need a photo: Este artículo necesita una foto
Do photo: Hacer foto Do photo: Hacer foto
Recycled Plastic: Plástico reciclado
Non recycled plastic: Plástico no reciclado

View File

@ -85,7 +85,7 @@
show-field="id" show-field="id"
value-field="id" value-field="id"
search-function="$ctrl.itemSearchFunc($search)" search-function="$ctrl.itemSearchFunc($search)"
on-change="$ctrl.upsertPrice(price, true)" ng-change="$ctrl.upsertPrice(price, true)"
order="id DESC" order="id DESC"
tabindex="1"> tabindex="1">
<tpl-item> <tpl-item>

View File

@ -113,9 +113,21 @@
<vn-label-value label="Weight/Piece" <vn-label-value label="Weight/Piece"
value="{{$ctrl.summary.item.weightByPiece}}"> value="{{$ctrl.summary.item.weightByPiece}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Units/Box"
value="{{$ctrl.summary.item.packingOut}}">
</vn-label-value>
<vn-label-value label="Expense" <vn-label-value label="Expense"
value="{{$ctrl.summary.item.expense.name}}"> value="{{$ctrl.summary.item.expense.name}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Generic"
value="{{$ctrl.summary.item.genericFk}}">
</vn-label-value>
<vn-label-value label="Recycled Plastic"
value="{{$ctrl.summary.item.recycledPlastic}}">
</vn-label-value>
<vn-label-value label="Non recycled plastic"
value="{{$ctrl.summary.item.nonRecycledPlastic}}">
</vn-label-value>
</vn-one> </vn-one>
<vn-one name="tags"> <vn-one name="tags">
<h4 ng-show="$ctrl.isBuyer || $ctrl.isReplenisher"> <h4 ng-show="$ctrl.isBuyer || $ctrl.isReplenisher">

View File

@ -0,0 +1,81 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('clone', {
description: 'Clones the selected routes',
accessType: 'WRITE',
accepts: [
{
arg: 'ids',
type: ['number'],
required: true,
description: 'The routes ids to clone'
},
{
arg: 'etd',
type: 'date',
required: true,
description: 'The estimated time of departure for all roadmaps'
}
],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/clone`,
verb: 'POST'
}
});
Self.clone = async(ids, etd) => {
const tx = await Self.beginTransaction({});
try {
const models = Self.app.models;
const options = {transaction: tx};
const originalRoadmaps = await models.Roadmap.find({
where: {id: {inq: ids}},
fields: [
'id',
'name',
'tractorPlate',
'trailerPlate',
'phone',
'supplierFk',
'etd',
'observations',
'price'],
include: [{
relation: 'expeditionTruck',
scope: {
fields: ['roadmapFk', 'warehouseFk', 'eta', 'description']
}
}]
}, options);
if (ids.length != originalRoadmaps.length)
throw new UserError(`The amount of roadmaps found don't match`);
for (const roadmap of originalRoadmaps) {
roadmap.id = undefined;
roadmap.etd = etd;
const clone = await models.Roadmap.create(roadmap, options);
const expeditionTrucks = roadmap.expeditionTruck();
expeditionTrucks.map(expeditionTruck => {
expeditionTruck.roadmapFk = clone.id;
return expeditionTruck;
});
await models.ExpeditionTruck.create(expeditionTrucks, options);
}
await tx.commit();
return true;
} catch (e) {
await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,109 @@
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

@ -0,0 +1,27 @@
module.exports = Self => {
Self.remoteMethod('sorted', {
description: 'Sort the vehicles by warehouse',
accessType: 'WRITE',
accepts: [{
arg: 'warehouseFk',
type: 'number'
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/sorted`,
verb: `POST`
}
});
Self.sorted = async warehouseFk => {
return Self.rawSql(`
SELECT v.id, v.warehouseFk, v.numberPlate, w.name
FROM vehicle v
JOIN warehouse w ON w.id = v.warehouseFk
ORDER BY v.warehouseFk = ? DESC, w.id, v.numberPlate ASC;
`, [warehouseFk]);
};
};

View File

@ -5,18 +5,22 @@
"AgencyTermConfig": { "AgencyTermConfig": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Route": { "DeliveryPoint": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Vehicle": { "ExpeditionTruck": {
"dataSource": "vn"
},
"Roadmap": {
"dataSource": "vn"
},
"Route": {
"dataSource": "vn" "dataSource": "vn"
}, },
"RouteLog": { "RouteLog": {
"dataSource": "vn" "dataSource": "vn"
}, },
"DeliveryPoint": { "Vehicle": {
"dataSource": "vn" "dataSource": "vn"
} }
} }

View File

@ -0,0 +1,43 @@
{
"name": "ExpeditionTruck",
"base": "VnModel",
"options": {
"mysql": {
"table": "expeditionTruck"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"roadmapFk": {
"type": "number"
},
"warehouseFk": {
"type": "number"
},
"eta": {
"type": "date"
},
"description": {
"type": "string"
},
"userFk": {
"type": "number"
}
},
"relations": {
"roadmap": {
"type": "belongsTo",
"model": "Roadmap",
"foreignKey": "roadmapFk"
},
"warehouse": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
}
}
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/roadmap/clone')(Self);
};

View File

@ -0,0 +1,63 @@
{
"name": "Roadmap",
"base": "VnModel",
"options": {
"mysql": {
"table": "roadmap"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"name": {
"type": "string"
},
"tractorPlate": {
"type": "string"
},
"trailerPlate": {
"type": "string"
},
"phone": {
"type": "string"
},
"supplierFk": {
"type": "number"
},
"etd": {
"type": "date"
},
"observations": {
"type": "string"
},
"userFk": {
"type": "number"
},
"price": {
"type": "number"
},
"driverName": {
"type": "string"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "userFk"
},
"supplier": {
"type": "belongsTo",
"model": "Supplier",
"foreignKey": "supplierFk"
},
"expeditionTruck": {
"type": "hasMany",
"model": "ExpeditionTruck",
"foreignKey": "roadmapFk"
}
}
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/vehicle/sorted')(Self);
};

View File

@ -23,12 +23,14 @@
</tpl-item> </tpl-item>
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete <vn-autocomplete
label="Vehicle"
ng-model="$ctrl.route.vehicleFk" ng-model="$ctrl.route.vehicleFk"
url="Vehicles" data="$ctrl.vehicles"
show-field="numberPlate" show-field="numberPlate"
value-field="id" value-field="id"
label="Vehicle" order="false"
vn-name="vehicle"> vn-name="vehicle">
<tpl-item>{{::numberPlate}} - {{::name}}</tpl-item>
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>

View File

@ -2,6 +2,13 @@ import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
class Controller extends Section { class Controller extends Section {
$onInit() {
this.$http.post(`Vehicles/sorted`, {warehouseFk: this.vnConfig.warehouseFk})
.then(res => {
this.vehicles = res.data;
});
}
onSubmit() { onSubmit() {
this.$.watcher.submit().then(() => this.$.watcher.submit().then(() =>
this.card.reload() this.card.reload()

View File

@ -16,3 +16,4 @@ import './agency-term/createInvoiceIn';
import './agency-term-search-panel'; import './agency-term-search-panel';
import './ticket-popup'; import './ticket-popup';
import './sms'; import './sms';
import './roadmap';

View File

@ -0,0 +1,98 @@
<mg-ajax path="Roadmaps/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.roadmap"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield vn-focus
vn-one
label="Roadmap"
ng-model="$ctrl.roadmap.name"
rule>
</vn-textfield>
<vn-date-picker
vn-one
label="ETD date"
ng-model="$ctrl.roadmap.etd">
</vn-date-picker>
<vn-input-time
vn-one
label="ETD hour"
ng-model="$ctrl.roadmap.etd">
</vn-input-time>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Tractor plate"
ng-model="$ctrl.roadmap.tractorPlate"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Trailer plate"
ng-model="$ctrl.roadmap.trailerPlate"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.roadmap.supplierFk"
url="Suppliers"
show-field="nickname"
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
value-field="id"
order="nickname"
label="Carrier">
<tpl-item>
{{::id}} - {{::nickname}}
</tpl-item>
</vn-autocomplete>
<vn-input-number
vn-one
label="Price"
ng-model="$ctrl.roadmap.price"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Driver name"
ng-model="$ctrl.roadmap.driverName"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Phone"
ng-model="$ctrl.roadmap.phone"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textArea
vn-one
label="Observations"
ng-model="$ctrl.roadmap.observations"
rule>
</vn-textArea>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -0,0 +1,16 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
this.$.watcher.submit();
}
}
ngModule.component('vnRoadmapBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -0,0 +1,5 @@
<vn-portal slot="menu">
<vn-roadmap-descriptor roadmap="$ctrl.roadmap"></vn-roadmap-descriptor>
<vn-left-menu source="roadmap"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -0,0 +1,19 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
const filter = {
include: [
{relation: 'supplier'}
]
};
this.$http.get(`Roadmaps/${this.$params.id}`, {filter})
.then(res => this.roadmap = res.data);
}
}
ngModule.vnComponent('vnRoadmapCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,37 @@
<vn-watcher
vn-id="watcher"
url="Roadmaps"
data="$ctrl.roadmap"
insert-mode="true"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
label="Roadmap"
ng-model="$ctrl.roadmap.name"
rule>
</vn-textfield>
<vn-date-picker
label="ETD date"
ng-model="$ctrl.roadmap.etd">
</vn-date-picker>
<vn-input-time
label="ETD hour"
ng-model="$ctrl.roadmap.etd">
</vn-input-time>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="roadmap.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -0,0 +1,23 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, $transclude, vnReport, vnEmail) {
super($element, $, $transclude);
this.roadmap = {etd: Date.vnNew()};
}
onSubmit() {
this.$.watcher.submit().then(
res => this.$state.go('route.roadmap.card.summary', {id: res.data.id})
);
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnRoadmapCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,6 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -0,0 +1,39 @@
<vn-descriptor-content
module="route"
base-state="route.roadmap"
description="$ctrl.roadmap.name">
<slot-menu>
<vn-item
ng-click="deleteRoadmap.show()"
name="deleteRoadmap"
translate>
Delete roadmap
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Roadmap"
value="{{$ctrl.roadmap.name}}">
</vn-label-value>
<vn-label-value
label="ETD"
value="{{$ctrl.roadmap.etd | date:'dd/MM/yyyy HH:mm'}}">
</vn-label-value>
<vn-label-value label="Carrier">
<span ng-click="supplierDescriptor.show($event, $ctrl.roadmap.supplier.id)" class="link">
{{$ctrl.roadmap.supplier.nickname}}
</span>
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm
vn-id="deleteRoadmap"
on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?"
message="The roadmap will be removed">
</vn-confirm>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -0,0 +1,26 @@
import ngModule from '../../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get roadmap() {
return this.entity;
}
set roadmap(value) {
this.entity = value;
}
onDelete() {
return this.$http.delete(`Roadmaps/${this.roadmap.id}`)
.then(() => this.$state.go('route.roadmap'))
.then(() => this.vnApp.showSuccess(this.$t('Roadmap removed')));
}
}
ngModule.component('vnRoadmapDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -0,0 +1,3 @@
Delete roadmap: Eliminar troncal
The roadmap will be removed: La troncal será eliminada
Roadmap removed: Troncal eliminada

View File

@ -0,0 +1,9 @@
import './main';
import './index/';
import './summary';
import './card';
import './descriptor';
import './create';
import './basic-data';
import './search-panel';
import './stops';

View File

@ -0,0 +1,112 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-lg">
<vn-card class="vn-pa-md vn-w-lg">
<vn-tool-bar class="vn-ma-md">
<vn-button
disabled="$ctrl.totalChecked == 0"
ng-click="$ctrl.openClonationDialog()"
icon="icon-clone"
vn-tooltip="Clone selected roadmaps">
</vn-button>
<vn-button
disabled="$ctrl.totalChecked == 0"
ng-click="deleteRoadmaps.show()"
vn-tooltip="Delete roadmap(s)"
icon="delete">
</vn-button>
</vn-tool-bar>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</vn-th>
<vn-th field="description">Roadmap</vn-th>
<vn-th field="etd" expand date>ETD</vn-th>
<vn-th field="supplierFk">Carrier</vn-th>
<vn-th field="plate">Plate</vn-th>
<vn-th field="price">Price</vn-th>
<vn-th field="observations" expand>Observations</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="roadmap in model.data"
class="clickable vn-tr search-result"
ui-sref="route.roadmap.card.summary({id: {{::roadmap.id}}})">
<vn-td>
<vn-check
ng-model="roadmap.checked"
vn-click-stop>
</vn-check>
</vn-td>
<vn-td>{{::roadmap.name}}</vn-td>
<vn-td expand date>{{::roadmap.etd | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td expand>
<span
class="link"
vn-click-stop="supplierDescriptor.show($event, roadmap.supplierFk)">
{{::roadmap.supplier.nickname}}
</span>
</vn-td>
<vn-td>{{::roadmap.tractorPlate | dashIfEmpty}}</vn-td>
<vn-td expand>{{::roadmap.price | currency: 'EUR':2 | dashIfEmpty}}</vn-td>
<vn-td expand>{{::roadmap.observations | dashIfEmpty}}</vn-td>
<vn-td shrink>
<vn-icon-button
vn-click-stop="$ctrl.preview(roadmap)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<a
ui-sref="route.roadmap.create"
vn-tooltip="Create roadmap"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-popup vn-id="summary">
<vn-roadmap-summary
roadmap="$ctrl.roadmapSelected">
</vn-roadmap-summary>
</vn-popup>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<!-- Clonation dialog -->
<vn-dialog class="edit"
vn-id="clonationDialog"
on-accept="$ctrl.cloneSelectedRoadmaps()"
message="Select the estimated time of departure (ETD)">
<tpl-body>
<vn-horizontal>
<vn-date-picker
label="ETD"
ng-model="$ctrl.etd">
</vn-date-picker>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Clone</button>
</tpl-buttons>
</vn-dialog>
<vn-confirm
vn-id="deleteRoadmaps"
question="Are you sure you want to continue?"
message="Selected roadmaps will be removed"
on-accept="$ctrl.deleteRoadmaps()">
</vn-confirm>

View File

@ -0,0 +1,62 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
get checked() {
const roadmaps = this.$.model.data || [];
const checkedRoadmap = [];
for (let roadmap of roadmaps) {
if (roadmap.checked)
checkedRoadmap.push(roadmap);
}
return checkedRoadmap;
}
get totalChecked() {
return this.checked.length;
}
preview(roadmap) {
this.roadmapSelected = roadmap;
this.$.summary.show();
}
openClonationDialog() {
this.$.clonationDialog.show();
this.etd = Date.vnNew();
}
cloneSelectedRoadmaps() {
try {
if (!this.etd)
throw new Error(`The date can't be empty`);
const roadmapsIds = [];
for (let roadmap of this.checked)
roadmapsIds.push(roadmap.id);
return this.$http.post('Roadmaps/clone', {ids: roadmapsIds, etd: this.etd}).then(() => {
this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
deleteRoadmaps() {
console.log(this.checked);
for (const roadmap of this.checked) {
this.$http.delete(`Roadmaps/${roadmap.id}`)
.then(() => this.$.model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('Roadmaps removed')));
}
}
}
ngModule.vnComponent('vnRoadmapIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,3 @@
Delete roadmap(s): Eliminar troncal(es)
Selected roadmaps will be removed: Los troncales seleccionados serán eliminados
Roadmaps removed: Troncales eliminados

View File

@ -0,0 +1,14 @@
Roadmaps: Troncales
Roadmap: Troncal
Driver name: Nombre conductor
Plate: Matrícula
Price: Precio
Observations: Observaciones
Clone selected roadmaps: Clonar troncales seleccionadas
Select the estimated time of departure (ETD): Seleccione la hora estimada de salida (ETD)
Create roadmap: Crear troncal
Tractor plate: Matrícula tractor
Trailer plate: Matrícula trailer
Carrier: Transportista
ETD date: Fecha ETD
ETD hour: Hora ETD

View File

@ -0,0 +1,20 @@
<vn-crud-model
vn-id="model"
url="Roadmaps"
include="$ctrl.include"
auto-load="true"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search roadmap by id or trunk"
panel="vn-roadmap-search-panel"
model="model"
filter="$ctrl.filterParams"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="route.roadmap">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-roadmap-index></vn-roadmap-index>
</ui-view>

View File

@ -0,0 +1,61 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class Roadmap extends ModuleMain {
constructor($element, $) {
super($element, $);
this.include = {
relation: 'supplier',
scope: {
fields: ['nickname']
}
};
}
$postLink() {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = Date.vnNew();
to.setHours(23, 59, 59, 999);
this.filterParams = {
from: from,
to: to
};
this.$.model.addFilter({where: {
and: [
{etd: {gte: from}},
{etd: {lte: to}}
]
}});
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {name: {like: `%${value}%`}};
case 'from':
return {etd: {gte: value}};
case 'to':
return {etd: {lte: value}};
case 'supplierFk':
case 'price':
return {[param]: value};
case 'tractorPlate':
case 'trailerPlate':
case 'phone':
case 'driverName':
return {[param]: {like: `%${value}%`}};
}
}
}
ngModule.vnComponent('vnRoadmap', {
controller: Roadmap,
template: require('./index.html')
});

View File

@ -0,0 +1 @@
Search roadmap by id or trunk: Buscar troncales por id o troncal

View File

@ -0,0 +1,74 @@
<div class="search-panel">
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
<vn-horizontal class="vn-px-lg vn-pt-lg">
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search routes by id"
vn-focus>
</vn-textfield>
</vn-horizontal>
<section class="vn-px-md">
<vn-horizontal class="manifold-panel vn-pa-md">
<vn-date-picker
vn-one
label="From"
ng-model="filter.from">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to">
</vn-date-picker>
</vn-horizontal>
</section>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Tractor plate"
ng-model="filter.tractorPlate">
</vn-textfield>
<vn-textfield
vn-one
label="Trailer plate"
ng-model="filter.trailerPlate">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-autocomplete
vn-one
ng-model="filter.supplierFk"
url="Suppliers"
show-field="nickname"
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
value-field="id"
order="nickname"
label="Carrier">
<tpl-item>
{{::id}} - {{::nickname}}
</tpl-item>
</vn-autocomplete>
<vn-input-number
vn-one
label="Price"
ng-model="filter.price">
</vn-input-number>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Driver name"
ng-model="filter.driverName">
</vn-textfield>
<vn-textfield
vn-one
label="Phone"
ng-model="filter.phone">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,7 @@
import ngModule from '../../module';
import SearchPanel from 'core/components/searchbar/search-panel';
ngModule.component('vnRoadmapSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -0,0 +1,71 @@
<vn-crud-model
vn-id="model"
url="ExpeditionTrucks"
where="{roadmapFk: $ctrl.$params.id}"
order="eta ASC"
data="$ctrl.expeditionTrucks"
auto-load="true">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="$ctrl.expeditionTrucks"
form="form">
</vn-watcher>
<form class="vn-w-md" name="form" ng-submit="$ctrl.onSubmit()">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="expeditionTruck in $ctrl.expeditionTrucks">
<vn-autocomplete vn-one
label="Warehouse"
ng-model="expeditionTruck.warehouseFk"
url="Warehouses"
show-field="name"
value-field="id"
vn-focus
rule>
</vn-autocomplete>
<vn-date-picker vn-one
label="ETA date"
ng-model="expeditionTruck.eta"
rule>
</vn-date-picker>
<vn-input-time
vn-one
label="ETA hour"
ng-model="expeditionTruck.eta">
</vn-input-time>
<vn-textArea
vn-one
label="Description"
ng-model="expeditionTruck.description"
rule>
</vn-textArea>
<vn-none>
<vn-icon-button
vn-tooltip="Remove stop"
icon="delete"
ng-click="model.remove($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-one>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add stop"
icon="add_circle"
ng-click="$ctrl.add()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
</vn-button-bar>
</form>
<vn-confirm
vn-id="confirm"
question="Delete stop?"
on-accept="$ctrl.removeTicketFromRoute($index)">
</vn-confirm>

View File

@ -0,0 +1,39 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
add() {
const filter = {
fields: ['etd']
};
this.$http.get(`Roadmaps/${this.$params.id}`, {filter})
.then(res => {
this.roadmap = res.data;
const eta = new Date(this.roadmap.etd);
eta.setDate(eta.getDate() + 1);
this.$.model.insert({
roadmapFk: this.$params.id,
eta: eta
});
});
}
onSubmit() {
this.$.watcher.check();
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
this.$.model.refresh();
});
}
}
ngModule.component('vnRoadmapStops', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -0,0 +1,4 @@
Remove stop: Eliminar parada
Add stop: Añadir parada
ETA date: Fecha ETA
ETA hour: Hora ETA

View File

@ -0,0 +1,113 @@
<vn-card class="summary">
<h5>
<span>{{summary.id}} - {{summary.name}}</span>
</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<vn-label-value label="Carrier">
<span ng-click="supplierDescriptor.show($event, summary.supplier.id)" class="link">
{{summary.supplier.nickname}}
</span>
</vn-label-value>
<vn-label-value
label="ETD"
value="{{summary.etd | date:'dd/MM/yyyy HH:mm'}}">
</vn-label-value>
<vn-label-value
label="Tractor plate"
value="{{summary.tractorPlate}}">
</vn-label-value>
<vn-label-value
label="Trailer plate"
value="{{summary.trailerPlate}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value
label="Phone"
value="{{summary.phone}}">
</vn-label-value>
<vn-label-value
label="Worker"
value="{{summary.worker.firstName}} {{summary.worker.lastName}}">
</vn-label-value>
<vn-label-value
label="Observations"
value="{{summary.observations}}">
</vn-label-value>
</vn-one>
<vn-auto>
<h4>
<a
ui-sref="route.roadmap.card.stops({id:summary.id})"
target="_self">
<span translate vn-tooltip="Go to">Stops</span>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add stop"
icon="add_circle"
vn-click-stop="addExpeditionTruck.show()">
</vn-icon-button>
</a>
</h4>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th>Wharehouse</vn-th>
<vn-th>ETA</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="expeditionTruck in summary.expeditionTruck">
<vn-td>{{expeditionTruck.warehouse.name}}</vn-td>
<vn-td expand>{{expeditionTruck.eta | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-auto>
</vn-horizontal>
</vn-card>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<vn-dialog
vn-id="addExpeditionTruck"
on-open="$ctrl.getETD()"
on-accept="$ctrl.onAddAccept()">
<tpl-body>
<vn-horizontal>
<vn-autocomplete
label="Warehouse"
ng-model="$ctrl.expeditionTruck.warehouseFk"
url="Warehouses"
show-field="name"
value-field="id"
vn-focus
rule>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
label="ETA date"
ng-model="$ctrl.expeditionTruck.eta"
rule>
</vn-date-picker>
<vn-input-time
label="ETA hour"
ng-model="$ctrl.expeditionTruck.eta">
</vn-input-time>
</vn-horizontal>
<vn-horizontal>
<vn-textArea
label="Description"
ng-model="$ctrl.expeditionTruck.description"
rule>
</vn-textArea>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Confirm</button>
</tpl-buttons>
</vn-dialog>

View File

@ -0,0 +1,68 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
set roadmap(value) {
this._roadmap = value;
this.$.summary = null;
if (!value) return;
this.loadData();
}
get roadmap() {
return this._roadmap;
}
loadData() {
const filter = {
include: [
{relation: 'supplier'},
{relation: 'worker'},
{relation: 'expeditionTruck',
scope: {
include: [
{relation: 'warehouse'}
]
}}
]
};
this.$http.get(`Roadmaps/${this.roadmap.id}`, {filter})
.then(res => this.$.summary = res.data);
}
getETD() {
const eta = new Date(this.roadmap.etd);
eta.setDate(eta.getDate() + 1);
this.expeditionTruck = {eta: eta};
}
onAddAccept() {
try {
const data = {
roadmapFk: this.roadmap.id,
warehouseFk: this.expeditionTruck.warehouseFk,
eta: this.expeditionTruck.eta,
description: this.expeditionTruck.description
};
this.$http.post(`ExpeditionTrucks`, data)
.then(() => {
this.loadData();
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
}
}
}
ngModule.component('vnRoadmapSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
roadmap: '<'
}
});

View File

@ -0,0 +1,3 @@
Stops: Paradas
Wharehouse: Almacén
You must fill all the fields: Debes rellenar todos los campos

View File

@ -0,0 +1,10 @@
@import "variables";
vn-roadmap-summary .summary {
a {
display: flex;
align-items: center;
height: 18.328px;
}
}

View File

@ -7,12 +7,17 @@
"menus": { "menus": {
"main": [ "main": [
{"state": "route.index", "icon": "icon-delivery"}, {"state": "route.index", "icon": "icon-delivery"},
{"state": "route.agencyTerm.index", "icon": "icon-agency-term"} {"state": "route.agencyTerm.index", "icon": "icon-agency-term"},
{"state": "route.roadmap", "icon": "icon-trailer"}
], ],
"card": [ "card": [
{"state": "route.card.basicData", "icon": "settings"}, {"state": "route.card.basicData", "icon": "settings"},
{"state": "route.card.tickets", "icon": "icon-ticket"}, {"state": "route.card.tickets", "icon": "icon-ticket"},
{"state": "route.card.log", "icon": "history"} {"state": "route.card.log", "icon": "history"}
],
"roadmap": [
{"state": "route.roadmap.card.basicData", "icon": "settings"},
{"state": "route.roadmap.card.stops", "icon": "icon-lines"}
] ]
}, },
"routes": [ "routes": [
@ -90,6 +95,46 @@
"route": "$ctrl.route" "route": "$ctrl.route"
}, },
"acl": ["delivery"] "acl": ["delivery"]
}, {
"url": "/roadmap?q",
"state": "route.roadmap",
"component": "vn-roadmap",
"description": "Roadmaps"
}, {
"url": "/create",
"state": "route.roadmap.create",
"component": "vn-roadmap-create",
"description": "Create roadmap"
},{
"url": "/:id",
"state": "route.roadmap.card",
"component": "vn-roadmap-card",
"abstract": true,
"description": "Detail"
},{
"url": "/summary",
"state": "route.roadmap.card.summary",
"component": "vn-roadmap-summary",
"description": "Summary",
"params": {
"roadmap": "$ctrl.roadmap"
}
},{
"url": "/basic-data",
"state": "route.roadmap.card.basicData",
"component": "vn-roadmap-basic-data",
"description": "Basic data",
"params": {
"roadmap": "$ctrl.roadmap"
}
}, {
"url": "/stops",
"state": "route.roadmap.card.stops",
"component": "vn-roadmap-stops",
"description": "Stops",
"params": {
"route": "$ctrl.roadmap"
}
} }
] ]
} }

View File

@ -7,7 +7,7 @@
ng-click="deleteShelving.show()" ng-click="deleteShelving.show()"
name="deleteShelving" name="deleteShelving"
translate> translate>
Delete Delete shelving
</vn-item> </vn-item>
</slot-menu> </slot-menu>
<slot-body> <slot-body>
@ -32,7 +32,7 @@
</slot-body> </slot-body>
</vn-descriptor-content> </vn-descriptor-content>
<vn-confirm <vn-confirm
vn-id="delete-shelving" vn-id="deleteShelving"
on-accept="$ctrl.onDelete()" on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?" question="Are you sure you want to continue?"
message="Shelving will be removed"> message="Shelving will be removed">

View File

@ -0,0 +1,3 @@
Delete shelving: Eliminar carro
Shelving will be removed: El carro será eliminado
Shelving removed: Carro eliminado

View File

@ -67,7 +67,7 @@ module.exports = function(Self) {
throw new UserError(`This ticket is already invoiced`); throw new UserError(`This ticket is already invoiced`);
const priceZero = ticket.totalWithVat == 0; const priceZero = ticket.totalWithVat == 0;
if (priceZero) if (ticketsIds.length == 1 && priceZero)
throw new UserError(`A ticket with an amount of zero can't be invoiced`); throw new UserError(`A ticket with an amount of zero can't be invoiced`);
}); });

View File

@ -60,6 +60,9 @@
}, },
"totalWithoutVat": { "totalWithoutVat": {
"type": "number" "type": "number"
},
"weight": {
"type": "number"
} }
}, },
"relations": { "relations": {

View File

@ -101,23 +101,33 @@
translate> translate>
Change shipped hour Change shipped hour
</vn-item> </vn-item>
<vn-item class="dropdown"
vn-click-stop="sendDeliveryNoteMenu.show($event, 'left');"
name="smsOptions"
translate>
Send SMS...
<vn-menu vn-id="sendDeliveryNoteMenu">
<vn-list>
<vn-item <vn-item
ng-click="$ctrl.sendPaymentSms()" ng-click="$ctrl.sendPaymentSms()"
name="sendPaymentSms" name="sendPaymentSms"
translate> translate>
SMS Pending payment Pending payment
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="$ctrl.sendImportSms()" ng-click="$ctrl.sendImportSms()"
name="sendImportSms" name="sendImportSms"
translate> translate>
SMS Minimum import Minimum import
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="$ctrl.sendChangesSms()" ng-click="$ctrl.sendChangesSms()"
name="sendChangesSms" name="sendChangesSms"
translate> translate>
SMS Notify changes Notify changes
</vn-item>
</vn-list>
</vn-menu>
</vn-item> </vn-item>
<vn-item <vn-item
ng-click="makeInvoiceConfirmation.show()" ng-click="makeInvoiceConfirmation.show()"
@ -163,6 +173,12 @@
</vn-list> </vn-list>
</vn-menu> </vn-menu>
</vn-item> </vn-item>
<vn-item
ng-click="setTicketWeight.show()"
vn-acl="deliveryBoss"
translate>
Set ticket weight
</vn-item>
</vn-list> </vn-list>
</vn-menu> </vn-menu>
@ -348,3 +364,28 @@
question="Are you sure you want to replace this delivery note?" question="Are you sure you want to replace this delivery note?"
message="Already exist signed delivery note"> message="Already exist signed delivery note">
</vn-confirm> </vn-confirm>
<!-- Set ticket weight -->
<vn-dialog
vn-id="setTicketWeight"
title="TicketWeight"
size="sm"
on-accept="$ctrl.setTicketWeight($ctrl.ticket.weight)">
<tpl-body>
<vn-input-number
label="Ticket weight"
ng-model="$ctrl.ticket.weight">
</vn-input-number>
</tpl-body>
<tpl-buttons>
<button response="accept" translate>Accept</button>
</tpl-buttons>
</vn-dialog>
<!-- Without weight confirmation dialog -->
<vn-confirm
vn-id="withoutWeightConfirmation"
on-accept="$ctrl.makeInvoice(true)"
question="This address has incoterms, you should set the weight before invoice it"
message="Are you sure you want to invoice this ticket?">
</vn-confirm>

View File

@ -249,16 +249,11 @@ class Controller extends Section {
this.$.sms.open(); this.$.sms.open();
} }
makeInvoice() { makeInvoice(force) {
const params = {ticketsIds: [this.id]}; if (this.ticket.address.incotermsFk && !this.ticket.weight && !force)
/* return this.$.withoutWeightConfirmation.show();
This should call the notification sistem to insert a new notification
in te queue, yet to check how to handle user permissions,
as of 08-11-2022 every employee can insert a new notification in the queue
*/
const client = this.ticket.client; const client = this.ticket.client;
if (client.hasElectronicInvoice) { if (client.hasElectronicInvoice) {
this.$http.post(`NotificationQueues`, { this.$http.post(`NotificationQueues`, {
notificationFk: 'invoice-electronic', notificationFk: 'invoice-electronic',
@ -275,7 +270,7 @@ class Controller extends Section {
}); });
} }
return this.$http.post(`Tickets/invoiceTickets`, params) return this.$http.post(`Tickets/invoiceTickets`, {ticketsIds: [this.id]})
.then(() => this.reload()) .then(() => this.reload())
.then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced'))); .then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced')));
} }
@ -331,6 +326,15 @@ class Controller extends Section {
this.vnApp.showSuccess(this.$t('PDF sent!')); this.vnApp.showSuccess(this.$t('PDF sent!'));
}); });
} }
setTicketWeight(weight) {
return this.$http.patch(`Tickets/${this.ticket.id}`, {weight})
.then(() => {
this.$.setTicketWeight.hide();
this.vnApp.showSuccess(this.$t('Data saved!'));
this.reload();
});
}
} }
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail']; Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];

View File

@ -198,6 +198,17 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
expect(controller.reload).toHaveBeenCalledWith(); expect(controller.reload).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalled(); expect(controller.vnApp.showSuccess).toHaveBeenCalled();
}); });
it('should show withoutWeightConfirmation', () => {
controller.$.withoutWeightConfirmation = {show: () => {}};
jest.spyOn(controller.$.withoutWeightConfirmation, 'show');
controller.ticket.address.incotermsFk = true;
controller.makeInvoice();
controller.ticket.address.incotermsFk = false;
expect(controller.$.withoutWeightConfirmation.show).toHaveBeenCalled();
});
}); });
describe('createPdfInvoice()', () => { describe('createPdfInvoice()', () => {

View File

@ -16,8 +16,14 @@ without warehouse: sin almacén
Invoice sent: Factura enviada Invoice sent: Factura enviada
The following refund ticket have been created: "Se ha creado siguiente ticket de abono: {{ticketId}}" The following refund ticket have been created: "Se ha creado siguiente ticket de abono: {{ticketId}}"
Transfer client: Transferir cliente Transfer client: Transferir cliente
SMS Notify changes: SMS Notificar cambios Send SMS...: Enviar SMS...
Notify changes: Notificar cambios
Minimum import: Importe mínimo
Pending payment: Pago pendiente
PDF sent!: ¡PDF enviado! PDF sent!: ¡PDF enviado!
Already exist signed delivery note: Ya existe albarán de entrega firmado Already exist signed delivery note: Ya existe albarán de entrega firmado
Are you sure you want to replace this delivery note?: ¿Seguro que quieres reemplazar este albarán de entrega? Are you sure you want to replace this delivery note?: ¿Seguro que quieres reemplazar este albarán de entrega?
Create a single ticket with all the content of the current ticket: Crea un ticket único con todo el contenido del ticket actual Create a single ticket with all the content of the current ticket: Crea un ticket único con todo el contenido del ticket actual
Set ticket weight: Establecer peso al ticket
Ticket weight: Peso del ticket
This address has incoterms, you should set the weight before invoice it: Este consigatario tiene incoterms, deberías establecer el peso antes de facturar

View File

@ -17,8 +17,6 @@ Shipped hour updated: Hora de envio modificada
Deleted ticket: Ticket eliminado Deleted ticket: Ticket eliminado
Recalculate components: Recalcular componentes Recalculate components: Recalcular componentes
Are you sure you want to recalculate the components?: ¿Seguro que quieres recalcular los componentes? Are you sure you want to recalculate the components?: ¿Seguro que quieres recalcular los componentes?
SMS Minimum import: 'SMS Importe minimo'
SMS Pending payment: 'SMS Pago pendiente'
Restore ticket: Restaurar ticket Restore ticket: Restaurar ticket
You are going to restore this ticket: Vas a restaurar este ticket You are going to restore this ticket: Vas a restaurar este ticket
Are you sure you want to restore this ticket?: ¿Seguro que quieres restaurar el ticket? Are you sure you want to restore this ticket?: ¿Seguro que quieres restaurar el ticket?

View File

@ -65,6 +65,11 @@
{{$ctrl.summary.refFk | dashIfEmpty}} {{$ctrl.summary.refFk | dashIfEmpty}}
</span> </span>
</vn-label-value> </vn-label-value>
<vn-label-value label="Weight">
<span>
{{$ctrl.summary.weight | dashIfEmpty}}
</span>
</vn-label-value>
</vn-one> </vn-one>
<vn-two> <vn-two>
<vn-label-value label="Shipped" <vn-label-value label="Shipped"

View File

@ -25,8 +25,7 @@
"type" : "string" "type" : "string"
}, },
"userFk": { "userFk": {
"type" : "number", "type" : "number"
"required": true
}, },
"bossFk": { "bossFk": {
"type" : "number" "type" : "number"

View File

@ -38,7 +38,7 @@
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
vn-one vn-one
label="Code" label="Worker code"
ng-model="$ctrl.worker.code" ng-model="$ctrl.worker.code"
maxLength="3" maxLength="3"
on-change="$ctrl.worker.code = $ctrl.worker.code.toUpperCase()" on-change="$ctrl.worker.code = $ctrl.worker.code.toUpperCase()"

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