diff --git a/.gitignore b/.gitignore index 3ef8c28fd..8cb76a829 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ coverage node_modules -dist/* +dist e2e/dms/*/ !e2e/dms/c4c !e2e/dms/c81 diff --git a/back/methods/chat/sendMessage.js b/back/methods/chat/sendMessage.js index f3510a098..2ab07af8c 100644 --- a/back/methods/chat/sendMessage.js +++ b/back/methods/chat/sendMessage.js @@ -28,8 +28,10 @@ module.exports = Self => { const models = Self.app.models; const accessToken = ctx.req.accessToken; const sender = await models.Account.findById(accessToken.userId); + const recipient = to.replace('@', ''); - return sendMessage(to, `@${sender.name}: ${message}`); + if (sender.name != recipient) + return sendMessage(to, `@${sender.name}: ${message}`); }; async function sendMessage(name, message) { @@ -70,6 +72,12 @@ module.exports = Self => { } function send(uri, body) { + if (process.env.NODE_ENV !== 'production') { + return new Promise(resolve => { + return resolve({statusCode: 200, message: 'Fake notification sent'}); + }); + } + const options = { method: 'POST', uri: uri, diff --git a/back/methods/chat/spec/send.spec.js b/back/methods/chat/spec/send.spec.js new file mode 100644 index 000000000..ebb62a0c8 --- /dev/null +++ b/back/methods/chat/spec/send.spec.js @@ -0,0 +1,18 @@ +const app = require('vn-loopback/server/server'); + +describe('chat sendMessage()', () => { + it('should return a "Fake notification sent" as response', async() => { + let ctx = {req: {accessToken: {userId: 1}}}; + let response = await app.models.Chat.sendMessage(ctx, '@salesPerson', 'I changed something'); + + expect(response.statusCode).toEqual(200); + expect(response.message).toEqual('Fake notification sent'); + }); + + it('should not return a response', async() => { + let ctx = {req: {accessToken: {userId: 18}}}; + let response = await app.models.Chat.sendMessage(ctx, '@salesPerson', 'I changed something'); + + expect(response).toBeUndefined(); + }); +}); diff --git a/back/models/account.js b/back/models/account.js index 9e6ecba45..00c583d31 100644 --- a/back/models/account.js +++ b/back/models/account.js @@ -62,7 +62,7 @@ module.exports = Self => { */ Self.hasRole = async function(userId, name) { let roles = await Self.getRoles(userId); - return roles.find(role => role == name); + return roles.some(role => role == name); }; /** diff --git a/db/changes/10100-AllSaints/00-zone.sql b/db/changes/10081-agency/00-zone.sql similarity index 71% rename from db/changes/10100-AllSaints/00-zone.sql rename to db/changes/10081-agency/00-zone.sql index 1fae52f4e..74e482c4c 100644 --- a/db/changes/10100-AllSaints/00-zone.sql +++ b/db/changes/10081-agency/00-zone.sql @@ -26,6 +26,12 @@ CREATE TABLE `vn`.`zoneEvent` ( CONSTRAINT `zoneEvent_ibfk_1` FOREIGN KEY (`zoneFk`) REFERENCES `zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8; +ALTER TABLE `vn`.`zoneEvent` + ADD COLUMN `type` ENUM('day', 'indefinitely', 'range') NOT NULL AFTER `zoneFk`, + ADD COLUMN `dated` DATE NULL DEFAULT NULL AFTER `type`, + CHANGE COLUMN `from` `started` DATE NULL DEFAULT NULL , + CHANGE COLUMN `to` `ended` DATE NULL DEFAULT NULL ; + CREATE TABLE `vn`.`zoneExclusion` ( `id` int(11) NOT NULL AUTO_INCREMENT, `zoneFk` int(11) NOT NULL, @@ -35,9 +41,16 @@ CREATE TABLE `vn`.`zoneExclusion` ( KEY `zoneFk` (`zoneFk`), CONSTRAINT `zoneExclusion_ibfk_1` FOREIGN KEY (`zoneFk`) REFERENCES `zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; +ALTER TABLE `vn`.`zone` +DROP FOREIGN KEY `fk_zone_1`; +ALTER TABLE `vn`.`zone` +CHANGE COLUMN `warehouseFk` `warehouseFk` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ; +ALTER TABLE `vn`.`zone` +ADD CONSTRAINT `fk_zone_1` + FOREIGN KEY (`warehouseFk`) + REFERENCES `vn`.`warehouse` (`id`) + ON DELETE NO ACTION + ON UPDATE CASCADE; -ALTER TABLE `vn`.`zone` - DROP FOREIGN KEY `fk_zone_1`; -ALTER TABLE `vn`.`zone` - DROP COLUMN `warehouseFk`, - DROP INDEX `fk_zone_1_idx`; +ALTER TABLE `vn`.`zoneExclusion` + CHANGE COLUMN `day` `dated` DATE NOT NULL ; diff --git a/db/changes/10081-agency/00-zone_getEvents.sql b/db/changes/10081-agency/00-zone_getEvents.sql index 199074a72..6e47bad22 100644 --- a/db/changes/10081-agency/00-zone_getEvents.sql +++ b/db/changes/10081-agency/00-zone_getEvents.sql @@ -1,10 +1,12 @@ +USE `vn`; +DROP procedure IF EXISTS `zone_getEvents`; -DROP PROCEDURE IF EXISTS `vn`.`zone_getEvents`; DELIMITER $$ -CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`zone_getEvents`( - vAgencyModeFk INT, +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `zone_getEvents`( vProvinceFk INT, - vPostCode VARCHAR(255)) + vPostCode VARCHAR(255), + vAgencyModeFk INT) BEGIN /** * Returns available events for the passed province/postcode and agency. @@ -13,9 +15,9 @@ BEGIN * @param vProvinceFk The province id * @param vPostCode The postcode or %NULL to use the province */ - DECLARE vGeoFk INT; - - IF vPostCode IS NOT NULL THEN + DECLARE vGeoFk INT; + + IF vPostCode IS NOT NULL THEN SELECT p.geoFk INTO vGeoFk FROM postCode p JOIN town t ON t.id = p.townFk @@ -24,24 +26,27 @@ BEGIN ELSE SELECT geoFk INTO vGeoFk FROM province - WHERE id = vProvinceFk; - END IF; + WHERE id = vProvinceFk; + END IF; - CALL zone_getFromGeo(vGeoFk); - - DELETE t FROM tmp.zone t - JOIN zone z ON z.id = t.id - WHERE z.agencyModeFk != vAgencyModeFk; - - SELECT e.`from`, e.`to`, e.weekDays + CALL zone_getFromGeo(vGeoFk); + + IF vAgencyModeFk IS NOT NULL THEN + DELETE t FROM tmp.zone t + JOIN zone z ON z.id = t.id + WHERE z.agencyModeFk != vAgencyModeFk; + END IF; + + SELECT e.`type`, e.dated, e.`started`, e.`ended`, e.weekDays FROM tmp.zone t JOIN zoneEvent e ON e.zoneFk = t.id; - - SELECT DISTINCT e.`day` + + SELECT DISTINCT e.dated FROM tmp.zone t JOIN zoneExclusion e ON e.zoneFk = t.id; DROP TEMPORARY TABLE tmp.zone; END$$ + DELIMITER ; diff --git a/db/changes/10081-agency/00-zone_getOptionsForDate.sql b/db/changes/10081-agency/00-zone_getOptionsForDate.sql deleted file mode 100644 index a71f85c1a..000000000 --- a/db/changes/10081-agency/00-zone_getOptionsForDate.sql +++ /dev/null @@ -1,79 +0,0 @@ - -DROP PROCEDURE IF EXISTS `vn`.`zone_getOptionsForLanding`; -DELIMITER $$ -CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`zone_getOptionsForLanding`(vLanded DATE) -BEGIN -/** - * Gets computed options for the passed zones and delivery date. - * - * @table tmp.zones(id) The zones ids - * @param vLanded The delivery date - * @return tmp.zoneOption The computed options - */ - DECLARE vHour TIME DEFAULT TIME(NOW()); - - DROP TEMPORARY TABLE IF EXISTS tTemp; - CREATE TEMPORARY TABLE tTemp - ENGINE = MEMORY - SELECT t.id zoneFk, - TIME(e.`hour`) `hour`, - e.travelingDays, - e.price, - e.bonus, - CASE - WHEN e.`from` IS NULL AND e.`to` IS NULL - THEN 3 - WHEN e.`to` IS NULL - THEN 2 - ELSE 1 - END specificity - FROM tmp.zone t - JOIN zoneEvent e ON e.zoneFk = t.id - WHERE (e.`from` = vLanded AND e.`to` IS NULL) - OR ( - (e.`from` IS NULL OR vLanded BETWEEN e.`from` AND e.`to`) - AND e.weekDays & (1 << WEEKDAY(vLanded)) - ); - - -- XXX: Compatibility with the deprecated #zoneCalendar table - - INSERT INTO tTemp - SELECT t.id zoneFk, - NULL, - NULL, - c.price, - c.bonus, - 4 - FROM tmp.zone t - JOIN zoneCalendar c ON c.zoneFk = t.id - WHERE c.delivered = vLanded; - - DELETE t FROM tTemp t - JOIN zoneExclusion e - ON e.zoneFk = t.zoneFk AND e.`day` = vLanded; - - UPDATE tTemp t - JOIN zone z ON z.id = t.zoneFk - SET t.`hour` = IFNULL(t.`hour`, TIME(z.`hour`)), - t.travelingDays = IFNULL(t.travelingDays, z.travelingDays), - t.price = IFNULL(t.price, z.price), - t.bonus = IFNULL(t.bonus, z.bonus); - - DELETE FROM tTemp - WHERE (@shipped := TIMESTAMPADD(DAY, -travelingDays, vLanded)) < CURDATE() - OR @shipped = CURDATE() AND vHour > `hour`; - - DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption; - CREATE TEMPORARY TABLE tmp.zoneOption - ENGINE = MEMORY - SELECT * - FROM ( - SELECT * FROM tTemp - ORDER BY zoneFk, specificity - ) t - GROUP BY zoneFk; - - DROP TEMPORARY TABLE tTemp; -END$$ -DELIMITER ; - diff --git a/db/changes/10100-AllSaints/01-zone_getAgency.sql b/db/changes/10081-agency/01-zone_getAgency.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getAgency.sql rename to db/changes/10081-agency/01-zone_getAgency.sql diff --git a/db/changes/10100-AllSaints/01-zone_getFirstShipped.sql b/db/changes/10081-agency/01-zone_getFirstShipped.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getFirstShipped.sql rename to db/changes/10081-agency/01-zone_getFirstShipped.sql diff --git a/db/changes/10100-AllSaints/01-zone_getLanded.sql b/db/changes/10081-agency/01-zone_getLanded.sql similarity index 96% rename from db/changes/10100-AllSaints/01-zone_getLanded.sql rename to db/changes/10081-agency/01-zone_getLanded.sql index fd3c6b6aa..a2bbe46a5 100644 --- a/db/changes/10100-AllSaints/01-zone_getLanded.sql +++ b/db/changes/10081-agency/01-zone_getLanded.sql @@ -28,7 +28,7 @@ BEGIN JOIN zone z ON z.id = zo.zoneFk JOIN zoneWarehouse zw ON zw.zoneFk = z.id WHERE agencyModeFk = vAgencyModeFk - AND warehouseFk = vWarehouseFk; + AND zw.warehouseFk = vWarehouseFk; DROP TEMPORARY TABLE tmp.zone, diff --git a/db/changes/10100-AllSaints/01-zone_getShippedWarehouse.sql b/db/changes/10081-agency/01-zone_getShippedWarehouse.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getShippedWarehouse.sql rename to db/changes/10081-agency/01-zone_getShippedWarehouse.sql diff --git a/db/changes/10100-AllSaints/01-zone_getWarehouse.sql b/db/changes/10081-agency/01-zone_getWarehouse.sql similarity index 100% rename from db/changes/10100-AllSaints/01-zone_getWarehouse.sql rename to db/changes/10081-agency/01-zone_getWarehouse.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetAgency.sql b/db/changes/10081-agency/02-zoneGetAgency.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetAgency.sql rename to db/changes/10081-agency/02-zoneGetAgency.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetFirstShipped__.sql b/db/changes/10081-agency/02-zoneGetFirstShipped__.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetFirstShipped__.sql rename to db/changes/10081-agency/02-zoneGetFirstShipped__.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetLanded.sql b/db/changes/10081-agency/02-zoneGetLanded.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetLanded.sql rename to db/changes/10081-agency/02-zoneGetLanded.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetShipped.sql b/db/changes/10081-agency/02-zoneGetShipped.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetShipped.sql rename to db/changes/10081-agency/02-zoneGetShipped.sql diff --git a/db/changes/10100-AllSaints/02-zoneGetShippedWarehouse.sql b/db/changes/10081-agency/02-zoneGetShippedWarehouse.sql similarity index 100% rename from db/changes/10100-AllSaints/02-zoneGetShippedWarehouse.sql rename to db/changes/10081-agency/02-zoneGetShippedWarehouse.sql diff --git a/db/changes/10081-agency/04-zone_getOptionsForLanding.sql b/db/changes/10081-agency/04-zone_getOptionsForLanding.sql new file mode 100644 index 000000000..ae081f14b --- /dev/null +++ b/db/changes/10081-agency/04-zone_getOptionsForLanding.sql @@ -0,0 +1,67 @@ +USE `vn`; +DROP procedure IF EXISTS `zone_getOptionsForLanding`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `zone_getOptionsForLanding`(vLanded DATE) +BEGIN +/** + * Gets computed options for the passed zones and delivery date. + * + * @table tmp.zones(id) The zones ids + * @param vLanded The delivery date + * @return tmp.zoneOption The computed options + */ + DECLARE vHour TIME DEFAULT TIME(NOW()); + + DROP TEMPORARY TABLE IF EXISTS tTemp; + CREATE TEMPORARY TABLE tTemp + ENGINE = MEMORY + SELECT t.id zoneFk, + IFNULL(TIME(e.`hour`), TIME(z.`hour`)) `hour`, + IFNULL(e.travelingDays, z.travelingDays) travelingDays, + IFNULL(e.price, z.price) price, + IFNULL(e.bonus, z.bonus) bonus, + CASE + WHEN e.`type` = 'day' + THEN 1 + WHEN e.`type` = 'range' + THEN 2 + ELSE 3 + END specificity + FROM tmp.zone t + JOIN zone z ON z.id = t.id + JOIN zoneEvent e ON e.zoneFk = t.id + WHERE ( + e.`type` = 'day' + AND e.dated = vLanded + ) OR ( + e.`type` != 'day' + AND e.weekDays & (1 << WEEKDAY(vLanded)) + AND (e.`started` IS NULL OR vLanded >= e.`started`) + AND (e.`ended` IS NULL OR vLanded <= e.`ended`) + ); + + DELETE t FROM tTemp t + JOIN zoneExclusion e + ON e.zoneFk = t.zoneFk AND e.`dated` = vLanded; + + DELETE FROM tTemp + WHERE (@shipped := TIMESTAMPADD(DAY, -travelingDays, vLanded)) < CURDATE() + OR @shipped = CURDATE() AND vHour > `hour`; + + DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption; + CREATE TEMPORARY TABLE tmp.zoneOption + ENGINE = MEMORY + SELECT * + FROM ( + SELECT * FROM tTemp + ORDER BY zoneFk, specificity + ) t + GROUP BY zoneFk; + + DROP TEMPORARY TABLE tTemp; +END$$ + +DELIMITER ; + diff --git a/db/changes/10081-agency/04-zone_getOptionsForShipment.sql b/db/changes/10081-agency/04-zone_getOptionsForShipment.sql new file mode 100644 index 000000000..c631f8809 --- /dev/null +++ b/db/changes/10081-agency/04-zone_getOptionsForShipment.sql @@ -0,0 +1,76 @@ +USE `vn`; +DROP procedure IF EXISTS `zone_getOptionsForShipment`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `zone_getOptionsForShipment`(vShipped DATE) +BEGIN +/** + * Gets computed options for the passed zones and shipping date. + * + * @table tmp.zones(id) The zones ids + * @param vShipped The shipping date + * @return tmp.zoneOption(zoneFk, hour, travelingDays, price, bonus, specificity) The computed options + */ + DROP TEMPORARY TABLE IF EXISTS tLandings; + CREATE TEMPORARY TABLE tLandings + (INDEX (eventFk)) + ENGINE = MEMORY + SELECT e.id eventFk, + @travelingDays := IFNULL(e.travelingDays, z.travelingDays) travelingDays, + TIMESTAMPADD(DAY, @travelingDays, vShipped) landed + FROM tmp.zone t + JOIN zone z ON z.id = t.id + JOIN zoneEvent e ON e.zoneFk = t.id; + + DROP TEMPORARY TABLE IF EXISTS tTemp; + CREATE TEMPORARY TABLE tTemp + ENGINE = MEMORY + SELECT t.id zoneFk, + IFNULL(TIME(e.`hour`), TIME(z.`hour`)) `hour`, + IFNULL(e.price, z.price) price, + IFNULL(e.bonus, z.bonus) bonus, + CASE + WHEN e.`type` = 'day' + THEN 1 + WHEN e.`type` = 'range' + THEN 2 + ELSE 3 + END specificity, + l.travelingDays, + l.landed + FROM tmp.zone t + JOIN zone z ON z.id = t.id + JOIN zoneEvent e ON e.zoneFk = t.id + JOIN tLandings l ON l.eventFk = e.id + WHERE ( + e.`type` = 'day' + AND e.`dated` = l.landed + ) OR ( + e.`type` != 'day' + AND e.weekDays & (1 << WEEKDAY(l.landed)) + AND (e.`started` IS NULL OR l.landed >= e.`started`) + AND (e.`ended` IS NULL OR l.landed <= e.`ended`) + ); + + DELETE t FROM tTemp t + JOIN zoneExclusion e + ON e.zoneFk = t.zoneFk AND e.`dated` = t.landed; + + DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption; + CREATE TEMPORARY TABLE tmp.zoneOption + ENGINE = MEMORY + SELECT * + FROM ( + SELECT * FROM tTemp + ORDER BY zoneFk, specificity + ) t + GROUP BY zoneFk; + + DROP TEMPORARY TABLE + tTemp, + tLandings; +END$$ + +DELIMITER ; + diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForDated.sql b/db/changes/10100-AllSaints/04-zone_getOptionsForDated.sql deleted file mode 100644 index fce54e012..000000000 --- a/db/changes/10100-AllSaints/04-zone_getOptionsForDated.sql +++ /dev/null @@ -1,88 +0,0 @@ -USE `vn`; -DROP procedure IF EXISTS `ticketCreateWithUser`; - -DELIMITER $$ -USE `vn`$$ -CREATE DEFINER=`root`@`%` PROCEDURE `ticketCreateWithUser`( - vClientId INT - ,vShipped DATE - ,vWarehouseFk INT - ,vCompanyFk INT - ,vAddressFk INT - ,vAgencyModeFk INT - ,vRouteFk INT - ,vlanded DATE - ,vUserId INT - ,OUT vNewTicket INT) -BEGIN - - DECLARE vZoneFk INT; - - IF vClientId IS NULL THEN - CALL util.throw ('CLIENT_NOT_ESPECIFIED'); - END IF; - - IF NOT vAddressFk OR vAddressFk IS NULL THEN - SELECT id INTO vAddressFk - FROM address - WHERE clientFk = vClientId AND isDefaultAddress; - END IF; - - IF vAgencyModeFk IS NOT NULL THEN - - CALL vn.zone_getShippedWarehouse(vlanded, vAddressFk, vAgencyModeFk); - - SELECT zoneFk INTO vZoneFk FROM tmp.zoneGetShipped - WHERE shipped = vShipped AND warehouseFk = vWarehouseFk LIMIT 1; - - IF vZoneFk IS NULL OR vZoneFk = 0 THEN - CALL util.throw ('NOT_ZONE_WITH_THIS_PARAMETERS'); - END IF; - END IF; - INSERT INTO vn2008.Tickets ( - Id_Cliente, - Fecha, - Id_Consigna, - Id_Agencia, - Alias, - warehouse_id, - Id_Ruta, - empresa_id, - landing, - zoneFk - ) - SELECT - vClientId, - vShipped, - a.id, - vAgencyModeFk, - a.nickname, - vWarehouseFk, - IF(vRouteFk,vRouteFk,NULL), - vCompanyFk, - vlanded, - vZoneFk - FROM address a - JOIN agencyMode am ON am.id = a.agencyModeFk - WHERE a.id = vAddressFk; - - SET vNewTicket = LAST_INSERT_ID(); - - INSERT INTO ticketObservation(ticketFk, observationTypeFk, description) - SELECT vNewTicket, ao.observationTypeFk, ao.description - FROM addressObservation ao - JOIN address a ON a.id = ao.addressFk - WHERE a.id = vAddressFk; - - INSERT INTO vn.ticketLog - SET originFk = vNewTicket, userFk = vUserId, `action` = 'insert', description = CONCAT('Ha creado el ticket:', ' ', vNewTicket); - - IF (SELECT ct.isCreatedAsServed FROM vn.clientType ct JOIN vn.client c ON c.typeFk = ct.code WHERE c.id = vClientId ) <> FALSE THEN - INSERT INTO vncontrol.inter(state_id, Id_Ticket, Id_Trabajador) - SELECT id, vNewTicket, getWorker() - FROM state - WHERE `code` = 'DELIVERED'; - END IF; -END$$ - -DELIMITER ; diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForLandingsql b/db/changes/10100-AllSaints/04-zone_getOptionsForLandingsql deleted file mode 100644 index 116703831..000000000 --- a/db/changes/10100-AllSaints/04-zone_getOptionsForLandingsql +++ /dev/null @@ -1,83 +0,0 @@ - -USE `vn`; -DROP procedure IF EXISTS `vn`.`zone_getOptionsForLanding`; - -DELIMITER $$ -USE `vn`$$ -CREATE DEFINER=`root`@`%` PROCEDURE `zone_getOptionsForLanding`(vLanded DATE) -BEGIN -/** - * Gets computed options for the passed zones and delivery date. - * - * @table tmp.zones(id) The zones ids - * @param vLanded The delivery date - * @return tmp.zoneOption The computed options - */ - DECLARE vHour TIME DEFAULT TIME(NOW()); - - DROP TEMPORARY TABLE IF EXISTS tTemp; - CREATE TEMPORARY TABLE tTemp - ENGINE = MEMORY - SELECT t.id zoneFk, - TIME(e.`hour`) `hour`, - e.travelingDays, - e.price, - e.bonus, - CASE - WHEN e.`from` IS NULL AND e.`to` IS NULL - THEN 3 - WHEN e.`to` IS NULL - THEN 2 - ELSE 1 - END specificity - FROM tmp.zone t - JOIN zoneEvent e ON e.zoneFk = t.id - WHERE (e.`from` = vLanded AND e.`to` IS NULL) - OR ( - (e.`from` IS NULL OR vLanded BETWEEN e.`from` AND e.`to`) - AND e.weekDays & (1 << WEEKDAY(vLanded)) - ); - - -- XXX: Compatibility with the deprecated #zoneCalendar table - - INSERT INTO tTemp - SELECT t.id zoneFk, - NULL, - NULL, - c.price, - c.bonus, - 4 - FROM tmp.zone t - JOIN zoneCalendar c ON c.zoneFk = t.id - WHERE c.delivered = vLanded; - - DELETE t FROM tTemp t - JOIN zoneExclusion e - ON e.zoneFk = t.zoneFk AND e.`day` = vLanded; - - UPDATE tTemp t - JOIN zone z ON z.id = t.zoneFk - SET t.`hour` = IFNULL(t.`hour`, TIME(z.`hour`)), - t.travelingDays = IFNULL(t.travelingDays, z.travelingDays), - t.price = IFNULL(t.price, z.price), - t.bonus = IFNULL(t.bonus, z.bonus); - - DELETE FROM tTemp - WHERE (@shipped := TIMESTAMPADD(DAY, -travelingDays, vLanded)) < CURDATE() - OR @shipped = CURDATE() AND vHour > `hour`; - - DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption; - CREATE TEMPORARY TABLE tmp.zoneOption - ENGINE = MEMORY - SELECT * - FROM ( - SELECT * FROM tTemp - ORDER BY zoneFk, specificity - ) t - GROUP BY zoneFk; - - DROP TEMPORARY TABLE tTemp; -END$$ - -DELIMITER ; -; diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForShipment.sql b/db/changes/10100-AllSaints/04-zone_getOptionsForShipment.sql deleted file mode 100644 index 26a2101f7..000000000 --- a/db/changes/10100-AllSaints/04-zone_getOptionsForShipment.sql +++ /dev/null @@ -1,65 +0,0 @@ -USE `vn`; -DROP procedure IF EXISTS `zone_getOptionsForShipment`; - -DELIMITER $$ -USE `vn`$$ -CREATE DEFINER=`root`@`%` PROCEDURE `zone_getOptionsForShipment`(vShipped DATE) -BEGIN -/** - * Gets computed options for the passed zones and shipping date. - * - * @table tmp.zones(id) The zones ids - * @param vShipped The shipping date - * @return tmp.zoneOption(zoneFk, hour, travelingDays, price, bonus, specificity) The computed options - */ - DECLARE vHour TIME DEFAULT TIME(NOW()); - - DROP TEMPORARY TABLE IF EXISTS tTemp; - CREATE TEMPORARY TABLE tTemp - ENGINE = MEMORY - SELECT t.id zoneFk, - TIME(e.`hour`) `hour`, - e.travelingDays, - e.price, - e.bonus, - CASE - WHEN e.`from` IS NULL AND e.`to` IS NULL - THEN 3 - WHEN e.`to` IS NULL - THEN 2 - ELSE 1 - END specificity - FROM tmp.zone t - JOIN zoneEvent e ON e.zoneFk = t.id - WHERE (e.`from` = TIMESTAMPADD(DAY, e.travelingDays, vShipped) AND e.`to` IS NULL) - OR ( - (e.`from` IS NULL OR TIMESTAMPADD(DAY, e.travelingDays, vShipped) BETWEEN e.`from` AND e.`to`) - AND e.weekDays & (1 << WEEKDAY(TIMESTAMPADD(DAY, e.travelingDays, vShipped))) - ); - - DELETE t FROM tTemp t - JOIN zoneExclusion e - ON e.zoneFk = t.zoneFk AND TIMESTAMPADD(DAY,-t.travelingDays, e.`day`) = vShipped; - - UPDATE tTemp t - JOIN zone z ON z.id = t.zoneFk - SET t.`hour` = IFNULL(t.`hour`, TIME(z.`hour`)), - t.travelingDays = IFNULL(t.travelingDays, z.travelingDays), - t.price = IFNULL(t.price, z.price), - t.bonus = IFNULL(t.bonus, z.bonus); - - DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption; - CREATE TEMPORARY TABLE tmp.zoneOption - ENGINE = MEMORY - SELECT * - FROM ( - SELECT * FROM tTemp - ORDER BY zoneFk, specificity - ) t - GROUP BY zoneFk; - - DROP TEMPORARY TABLE tTemp; -END$$ - -DELIMITER ; - diff --git a/db/changes/10100-AllSaints/00-ACL.sql b/db/changes/10100-allSaints/00-ACL.sql similarity index 100% rename from db/changes/10100-AllSaints/00-ACL.sql rename to db/changes/10100-allSaints/00-ACL.sql diff --git a/db/changes/10100-AllSaints/00-travelLog.sql b/db/changes/10100-allSaints/00-travelLog.sql similarity index 100% rename from db/changes/10100-AllSaints/00-travelLog.sql rename to db/changes/10100-allSaints/00-travelLog.sql diff --git a/db/changes/10100-AllSaints/00-upperGap.sql b/db/changes/10100-allSaints/00-upperGap.sql similarity index 100% rename from db/changes/10100-AllSaints/00-upperGap.sql rename to db/changes/10100-allSaints/00-upperGap.sql diff --git a/db/changes/10100-AllSaints/00-userLog.sql b/db/changes/10100-allSaints/00-userLog.sql similarity index 100% rename from db/changes/10100-AllSaints/00-userLog.sql rename to db/changes/10100-allSaints/00-userLog.sql diff --git a/db/changes/10100-AllSaints/00-userPhoneType.sql b/db/changes/10100-allSaints/00-userPhoneType.sql similarity index 100% rename from db/changes/10100-AllSaints/00-userPhoneType.sql rename to db/changes/10100-allSaints/00-userPhoneType.sql diff --git a/db/changes/10100-AllSaints/01-userPhone.sql b/db/changes/10100-allSaints/01-userPhone.sql similarity index 100% rename from db/changes/10100-AllSaints/01-userPhone.sql rename to db/changes/10100-allSaints/01-userPhone.sql diff --git a/db/changes/10100-AllSaints/02-catalog_componentCalculate.sql b/db/changes/10100-allSaints/02-catalog_componentCalculate.sql similarity index 100% rename from db/changes/10100-AllSaints/02-catalog_componentCalculate.sql rename to db/changes/10100-allSaints/02-catalog_componentCalculate.sql diff --git a/db/changes/10100-AllSaints/02-ticketCalculate__.sql b/db/changes/10100-allSaints/02-ticketCalculate__.sql similarity index 100% rename from db/changes/10100-AllSaints/02-ticketCalculate__.sql rename to db/changes/10100-allSaints/02-ticketCalculate__.sql diff --git a/db/changes/10100-AllSaints/03-available_calc.sql b/db/changes/10100-allSaints/03-available_calc.sql similarity index 100% rename from db/changes/10100-AllSaints/03-available_calc.sql rename to db/changes/10100-allSaints/03-available_calc.sql diff --git a/db/changes/10100-AllSaints/03-catalog_calculate.sql b/db/changes/10100-allSaints/03-catalog_calculate.sql similarity index 100% rename from db/changes/10100-AllSaints/03-catalog_calculate.sql rename to db/changes/10100-allSaints/03-catalog_calculate.sql diff --git a/db/changes/10100-AllSaints/03-order_confirmWithUser.sql b/db/changes/10100-allSaints/03-order_confirmWithUser.sql similarity index 100% rename from db/changes/10100-AllSaints/03-order_confirmWithUser.sql rename to db/changes/10100-allSaints/03-order_confirmWithUser.sql diff --git a/db/changes/10100-AllSaints/03-ticketCalculateClon.sql b/db/changes/10100-allSaints/03-ticketCalculateClon.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketCalculateClon.sql rename to db/changes/10100-allSaints/03-ticketCalculateClon.sql diff --git a/db/changes/10100-AllSaints/03-ticketComponentCalculate.sql b/db/changes/10100-allSaints/03-ticketComponentCalculate.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketComponentCalculate.sql rename to db/changes/10100-allSaints/03-ticketComponentCalculate.sql diff --git a/db/changes/10100-AllSaints/03-ticketCreateWithUser.sql b/db/changes/10100-allSaints/03-ticketCreateWithUser.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketCreateWithUser.sql rename to db/changes/10100-allSaints/03-ticketCreateWithUser.sql diff --git a/db/changes/10100-AllSaints/03-ticketCreateWithoutZone.sql b/db/changes/10100-allSaints/03-ticketCreateWithoutZone.sql similarity index 100% rename from db/changes/10100-AllSaints/03-ticketCreateWithoutZone.sql rename to db/changes/10100-allSaints/03-ticketCreateWithoutZone.sql diff --git a/db/changes/10100-AllSaints/05-catalog_calcFromItem.sql b/db/changes/10100-allSaints/05-catalog_calcFromItem.sql similarity index 100% rename from db/changes/10100-AllSaints/05-catalog_calcFromItem.sql rename to db/changes/10100-allSaints/05-catalog_calcFromItem.sql diff --git a/db/changes/10110-postCampaign/00-country.sql b/db/changes/10110-postCampaign/00-country.sql new file mode 100644 index 000000000..dcc883ab8 --- /dev/null +++ b/db/changes/10110-postCampaign/00-country.sql @@ -0,0 +1,10 @@ +USE `vn`; + +UPDATE `vn`.`country` SET `ibanLength` = '24' WHERE (`id` = 1); +UPDATE `vn`.`country` SET `ibanLength` = '27' WHERE (`id` = 2); +UPDATE `vn`.`country` SET `ibanLength` = '22' WHERE (`id` = 3); +UPDATE `vn`.`country` SET `ibanLength` = '24' WHERE (`id` = 4); +UPDATE `vn`.`country` SET `ibanLength` = '18' WHERE (`id` = 5); +UPDATE `vn`.`country` SET `ibanLength` = '25' WHERE (`id` = 8); +UPDATE `vn`.`country` SET `ibanLength` = '27' WHERE (`id` = 19); +UPDATE `vn`.`country` SET `ibanLength` = '24' WHERE (`id` = 30); diff --git a/db/changes/10110-postCampaign/00-department_doCalc.sql b/db/changes/10110-postCampaign/00-department_doCalc.sql new file mode 100644 index 000000000..7adeb30f0 --- /dev/null +++ b/db/changes/10110-postCampaign/00-department_doCalc.sql @@ -0,0 +1,3 @@ +USE `vn`; + +CREATE DEFINER=`root`@`%` EVENT `department_doCalc` ON SCHEDULE EVERY 15 SECOND STARTS '2019-11-15 00:00:00' ON COMPLETION PRESERVE ENABLE DO CALL vn.department_doCalc \ No newline at end of file diff --git a/db/changes/10110-postCampaign/00-sample.sql b/db/changes/10110-postCampaign/00-sample.sql new file mode 100644 index 000000000..68e9d1a0f --- /dev/null +++ b/db/changes/10110-postCampaign/00-sample.sql @@ -0,0 +1,8 @@ +USE `vn`; + +UPDATE `vn`.`sample` SET `description` = 'Bienvenida como nuevo cliente' WHERE (`id` = '12'); +UPDATE `vn`.`sample` SET `description` = 'Instalación y configuración de impresora de coronas' WHERE (`id` = '13'); +UPDATE `vn`.`sample` SET `description` = 'Solicitud de domiciliación bancaria' WHERE (`id` = '14'); +UPDATE `vn`.`sample` SET `description` = 'Aviso inicial por saldo deudor' WHERE (`id` = '15'); +UPDATE `vn`.`sample` SET `description` = 'Aviso reiterado por saldo deudor' WHERE (`id` = '16'); +UPDATE `vn`.`sample` SET `isVisible` = '0' WHERE (`id` = '17'); diff --git a/db/changes/10110-postCampaign/00-ticketClosure.sql b/db/changes/10110-postCampaign/00-ticketClosure.sql new file mode 100644 index 000000000..07c7ef812 --- /dev/null +++ b/db/changes/10110-postCampaign/00-ticketClosure.sql @@ -0,0 +1,116 @@ +USE `vn`; +DROP procedure IF EXISTS `ticketClosure`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `ticketClosure`() +BEGIN +/** + * Realiza el cierre de todos los + * tickets de la table ticketClosure. + */ + DECLARE vDone BOOL; + DECLARE vClientFk INT; + DECLARE vTicketFk INT; + DECLARE vIsTaxDataChecked BOOL; + DECLARE vCompanyFk INT; + DECLARE vShipped DATE; + DECLARE vPriority INT DEFAULT 1; + DECLARE vReportDeliveryNote INT DEFAULT 1; + DECLARE vNewInvoiceId INT; + DECLARE vHasDailyInvoice BOOL; + DECLARE vWithPackage BOOL; + + DECLARE cur CURSOR FOR + SELECT ticketFk FROM tmp.ticketClosure; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN + RESIGNAL; + END; + + DROP TEMPORARY TABLE IF EXISTS tmp.ticketClosure2; + CREATE TEMPORARY TABLE tmp.ticketClosure2 + SELECT ticketFk FROM tmp.ticketClosure; + INSERT INTO tmp.ticketClosure + SELECT id FROM stowaway s + JOIN tmp.ticketClosure2 tc ON s.shipFk = tc.ticketFk; + OPEN cur; + + proc: LOOP + SET vDone = FALSE; + + FETCH cur INTO vTicketFk; + + IF vDone THEN + LEAVE proc; + END IF; + + -- ticketClosure start + SELECT + c.id, + c.isTaxDataChecked, + t.companyFk, + t.shipped, + co.hasDailyInvoice, + w.isManaged + INTO vClientFk, + vIsTaxDataChecked, + vCompanyFk, + vShipped, + vHasDailyInvoice, + vWithPackage + FROM ticket t + JOIN `client` c ON c.id = t.clientFk + JOIN province p ON p.id = c.provinceFk + JOIN country co ON co.id = p.countryFk + JOIN warehouse w ON w.id = t.warehouseFk + WHERE t.id = vTicketFk; + + INSERT INTO ticketPackaging (ticketFk, packagingFk, quantity) + (SELECT vTicketFk, p.id, COUNT(*) + FROM expedition e + JOIN packaging p ON p.itemFk = e.itemFk + WHERE e.ticketFk = vTicketFk AND p.isPackageReturnable + AND vWithPackage + GROUP BY p.itemFk); + + -- No retornables o no catalogados + INSERT INTO sale (itemFk, ticketFk, concept, quantity, price, isPriceFixed) + (SELECT e.itemFk, vTicketFk, i.name, COUNT(*) AS amount, getSpecialPrice(e.itemFk, vClientFk), 1 + FROM expedition e + JOIN item i ON i.id = e.itemFk + LEFT JOIN packaging p ON p.itemFk = i.id + WHERE e.ticketFk = vTicketFk AND IFNULL(p.isPackageReturnable, 0) = 0 + AND getSpecialPrice(e.itemFk, vClientFk) > 0 + GROUP BY e.itemFk); + + IF(vHasDailyInvoice) THEN + + -- Facturacion rapida + CALL ticketTrackingAdd(vTicketFk, 'DELIVERED', NULL); + -- Facturar si está contabilizado + IF vIsTaxDataChecked THEN + CALL invoiceOut_newFromClient( + vClientFk, + (SELECT invoiceSerial(vClientFk, vCompanyFk, 'M')), + vShipped, + vCompanyFk, + NULL, + vNewInvoiceId); + END IF; + ELSE + -- Albaran_print + CALL ticketTrackingAdd(vTicketFk, (SELECT vn.getAlert3State(vTicketFk)), NULL); + END IF; + + -- ticketClosure end + END LOOP; + + CLOSE cur; + + DROP TEMPORARY TABLE IF EXISTS tmp.ticketClosure2; +END$$ + +DELIMITER ; + diff --git a/db/changes/10110-postCampaign/00-ticketRequest.sql b/db/changes/10110-postCampaign/00-ticketRequest.sql new file mode 100644 index 000000000..0b8a5d22d --- /dev/null +++ b/db/changes/10110-postCampaign/00-ticketRequest.sql @@ -0,0 +1,108 @@ +USE `vn`; + +ALTER TABLE `vn`.`ticketRequest` +DROP FOREIGN KEY `fgnAtender`; +ALTER TABLE `vn`.`ticketRequest` +CHANGE COLUMN `atenderFk` `attenderFk` INT(11) NULL DEFAULT NULL ; +ALTER TABLE `vn`.`ticketRequest` +ADD CONSTRAINT `fgnAtender` + FOREIGN KEY (`attenderFk`) + REFERENCES `vn`.`worker` (`id`) + ON UPDATE CASCADE; + +USE `vn2008`; +CREATE + OR REPLACE ALGORITHM = UNDEFINED + DEFINER = `root`@`%` + SQL SECURITY DEFINER +VIEW `vn2008`.`Ordenes` AS + SELECT + `tr`.`id` AS `Id_ORDEN`, + `tr`.`description` AS `ORDEN`, + `tr`.`requesterFk` AS `requesterFk`, + `tr`.`attenderFk` AS `attenderFk`, + `tr`.`quantity` AS `CANTIDAD`, + `tr`.`itemFk` AS `Id_ARTICLE`, + `tr`.`price` AS `PRECIOMAX`, + `tr`.`isOk` AS `isOk`, + `tr`.`saleFk` AS `Id_Movimiento`, + `tr`.`ticketFk` AS `ticketFk`, + `tr`.`response` AS `COMENTARIO`, + `tr`.`created` AS `odbc_date`, + `tr`.`ordered` AS `datORDEN`, + `tr`.`shipped` AS `datTICKET`, + `tr`.`salesPersonCode` AS `CodVENDEDOR`, + `tr`.`buyerCode` AS `CodCOMPRADOR`, + `tr`.`price__` AS `PREU`, + `tr`.`clientFk` AS `Id_CLIENTE`, + `tr`.`ok__` AS `OK`, + `tr`.`total` AS `TOTAL`, + `tr`.`buyed` AS `datCOMPRA`, + `tr`.`ko__` AS `KO` + FROM + `vn`.`ticketRequest` `tr`; + +USE `vn`; + +DROP TRIGGER IF EXISTS `vn`.`ticketRequest_beforeInsert`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` TRIGGER `vn`.`ticketRequest_beforeInsert` BEFORE INSERT ON `ticketRequest` FOR EACH ROW +BEGIN + IF NEW.ticketFk IS NULL THEN + SET NEW.ticketFk = (SELECT s.ticketFk FROM sale s WHERE s.id = NEW.saleFk); + END IF; + + IF NEW.requesterFk IS NULL THEN + SET NEW.requesterFk = (SELECT w.id FROM worker w WHERE w.code = NEW.salesPersonCode); + END IF; + + IF NEW.attenderFk IS NULL THEN + SET NEW.attenderFk = (SELECT w.id FROM worker w WHERE w.code = NEW.buyerCode); + END IF; +END$$ +DELIMITER ; + + +DROP TRIGGER IF EXISTS `vn`.`ticketRequest_beforeUpdate`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` TRIGGER `vn`.`ticketRequest_beforeUpdate` BEFORE UPDATE ON `ticketRequest` FOR EACH ROW +BEGIN + IF NEW.saleFk <> OLD.saleFk THEN + SET NEW.ticketFk = (SELECT s.ticketFk FROM sale s WHERE s.id = NEW.saleFk); + END IF; + + IF NEW.salesPersonCode <> OLD.salesPersonCode THEN + SET NEW.requesterFk = (SELECT w.id FROM worker w WHERE w.code = NEW.salesPersonCode); + END IF; + + IF NEW.buyerCode <> OLD.buyerCode THEN + SET NEW.attenderFk = (SELECT w.id FROM worker w WHERE w.code = NEW.buyerCode); + END IF; +END$$ +DELIMITER ; + +USE `vn`; +CREATE + OR REPLACE ALGORITHM = UNDEFINED + DEFINER = `root`@`%` + SQL SECURITY DEFINER +VIEW `vn`.`ticketRequest__` AS + SELECT + `t`.`Id_ORDEN` AS `id`, + `t`.`ORDEN` AS `description`, + `t`.`requesterFk` AS `requesterFk`, + `t`.`attenderFk` AS `attenderFk`, + `t`.`CANTIDAD` AS `quantity`, + `t`.`Id_ARTICLE` AS `itemFk`, + `t`.`PRECIOMAX` AS `price`, + `t`.`isOk` AS `isOk`, + `t`.`Id_Movimiento` AS `saleFk`, + `t`.`ticketFk` AS `ticketFk`, + `t`.`COMENTARIO` AS `response`, + `t`.`odbc_date` AS `created` + FROM + `vn2008`.`Ordenes` `t`; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 1c464589f..d2c4050e2 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -53,13 +53,14 @@ INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossF INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`) VALUES - (1, 'España', 0, 'ES', 1, 22), - (2, 'Italia', 1, 'IT', 1, 25), - (3, 'Alemania', 1, 'DE', 1, 20), - (4, 'Rumania', 1, 'RO', 1, 22), - (5, 'Holanda', 1, 'NL', 1, 16), - (19,'Francia', 1, 'FR', 1, 25), - (30,'Canarias', 1, 'IC', 1, 22); + (1, 'España', 0, 'ES', 1, 24), + (2, 'Italia', 1, 'IT', 1, 27), + (3, 'Alemania', 1, 'DE', 1, 22), + (4, 'Rumania', 1, 'RO', 1, 24), + (5, 'Holanda', 1, 'NL', 1, 18), + (8, 'Portugal', 1, 'PT', 1, 27), + (19,'Francia', 1, 'FR', 1, 27), + (30,'Canarias', 1, 'IC', 1, 24); INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`) VALUES @@ -195,9 +196,9 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city VALUES (101, 'Bruce Wayne', '84612325V', 'Batman', 'Alfred', '1007 Mountain Drive, Gotham', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), (102, 'Petter Parker', '87945234L', 'Spider man', 'Aunt May', '20 Ingram Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), - (103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 0, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), + (103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 0, 19, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), (104, 'Tony Stark', '06089160W', 'Iron man', 'Pepper Potts', '10880 Malibu Point', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), - (105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1), + (105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 8, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1), (106, 'DavidCharlesHaller', '53136686Q', 'Legion', 'Charles Xavier', 'Evil hideout', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1), (107, 'Hank Pym', '09854837G', 'Ant man', 'Hawk', 'Anthill', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1), (108, 'Charles Xavier', '22641921P', 'Professor X', 'Beast', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1), @@ -1467,7 +1468,7 @@ INSERT INTO `vn2008`.`workerTeam`(`id`, `team`, `user`) (5, 3, 103), (6, 3, 104); -INSERT INTO `vn`.`ticketRequest`(`id`, `description`, `requesterFk`, `atenderFk`, `quantity`, `itemFk`, `price`, `isOk`, `saleFk`, `ticketFk`, `created`) +INSERT INTO `vn`.`ticketRequest`(`id`, `description`, `requesterFk`, `attenderFk`, `quantity`, `itemFk`, `price`, `isOk`, `saleFk`, `ticketFk`, `created`) VALUES (1, 'Ranged weapon longbow 2m', 18, 35, 5, 1, 9.10, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (2, 'Melee weapon combat first 15cm', 18, 35, 10, 2, 1.07, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), @@ -1614,227 +1615,227 @@ INSERT INTO `vn`.`zoneIncluded` (`zoneFk`, `geoFk`, `isIncluded`) (8, 5, 0), (8, 1, 1); -INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `from`) +INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `dated`) VALUES - (1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=2, 2, 9 ) - DAYOFWEEK(CURDATE())) DAY)), - (1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=3, 3, 10) - DAYOFWEEK(CURDATE())) DAY)), - (1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=4, 4, 11) - DAYOFWEEK(CURDATE())) DAY)), - (1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=5, 5, 12) - DAYOFWEEK(CURDATE())) DAY)), - (1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=6, 6, 13) - DAYOFWEEK(CURDATE())) DAY)), - (2, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=2, 2, 9 ) - DAYOFWEEK(CURDATE())) DAY)), - (2, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=3, 3, 10) - DAYOFWEEK(CURDATE())) DAY)), - (2, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=4, 4, 11) - DAYOFWEEK(CURDATE())) DAY)), - (2, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=5, 5, 12) - DAYOFWEEK(CURDATE())) DAY)), - (2, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=6, 6, 13) - DAYOFWEEK(CURDATE())) DAY)), - (3, CURDATE()), - (3, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +2 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +3 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +4 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +5 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +6 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +7 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +8 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +9 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +10 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +11 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +12 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +13 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +14 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +15 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +16 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +17 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +18 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +19 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +20 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +21 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +22 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +23 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +24 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +25 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +26 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +27 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +28 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +29 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +30 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +31 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +32 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +33 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +34 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +35 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +36 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +37 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +38 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +39 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +40 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +41 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +42 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +43 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +44 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +45 DAY)), - (3, DATE_ADD(CURDATE(), INTERVAL +46 DAY)), - (4, CURDATE()), - (4, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +2 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +3 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +4 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +5 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +6 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +7 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +8 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +9 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +10 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +11 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +12 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +13 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +14 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +15 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +16 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +17 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +18 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +19 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +20 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +21 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +22 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +23 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +24 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +25 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +26 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +27 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +28 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +29 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +30 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +31 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +32 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +33 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +34 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +35 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +36 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +37 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +38 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +39 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +40 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +41 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +42 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +43 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +44 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +45 DAY)), - (4, DATE_ADD(CURDATE(), INTERVAL +46 DAY)), - (5, CURDATE()), - (5, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +2 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +3 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +4 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +5 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +6 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +7 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +8 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +9 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +10 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +11 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +12 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +13 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +14 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +15 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +16 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +17 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +18 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +19 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +20 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +21 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +22 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +23 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +24 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +25 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +26 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +27 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +28 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +29 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +30 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +31 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +32 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +33 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +34 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +35 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +36 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +37 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +38 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +39 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +40 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +41 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +42 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +43 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +44 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +45 DAY)), - (5, DATE_ADD(CURDATE(), INTERVAL +46 DAY)), - (6, CURDATE()), - (6, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +2 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +3 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +4 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +5 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +6 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +7 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +8 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +9 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +10 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +11 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +12 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +13 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +14 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +15 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +16 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +17 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +18 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +19 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +20 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +21 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +22 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +23 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +24 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +25 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +26 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +27 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +28 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +29 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +30 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +31 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +32 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +33 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +34 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +35 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +36 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +37 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +38 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +39 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +40 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +41 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +42 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +43 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +44 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +45 DAY)), - (6, DATE_ADD(CURDATE(), INTERVAL +46 DAY)), - (7, CURDATE()), - (7, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), - (7, DATE_ADD(CURDATE(), INTERVAL +2 DAY)), - (7, DATE_ADD(CURDATE(), INTERVAL +3 DAY)), - (7, DATE_ADD(CURDATE(), INTERVAL +4 DAY)), - (7, DATE_ADD(CURDATE(), INTERVAL +5 DAY)), - (7, DATE_ADD(CURDATE(), INTERVAL +6 DAY)), - (8, CURDATE()), - (8, DATE_ADD(CURDATE(), INTERVAL +1 DAY)), - (8, DATE_ADD(CURDATE(), INTERVAL +2 DAY)), - (8, DATE_ADD(CURDATE(), INTERVAL +3 DAY)), - (8, DATE_ADD(CURDATE(), INTERVAL +4 DAY)), - (8, DATE_ADD(CURDATE(), INTERVAL +5 DAY)), - (8, DATE_ADD(CURDATE(), INTERVAL +6 DAY)); + (1, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=2, 2, 9 ) - DAYOFWEEK(CURDATE())) DAY)), + (1, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=3, 3, 10) - DAYOFWEEK(CURDATE())) DAY)), + (1, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=4, 4, 11) - DAYOFWEEK(CURDATE())) DAY)), + (1, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=5, 5, 12) - DAYOFWEEK(CURDATE())) DAY)), + (1, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=6, 6, 13) - DAYOFWEEK(CURDATE())) DAY)), + (2, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=2, 2, 9 ) - DAYOFWEEK(CURDATE())) DAY)), + (2, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=3, 3, 10) - DAYOFWEEK(CURDATE())) DAY)), + (2, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=4, 4, 11) - DAYOFWEEK(CURDATE())) DAY)), + (2, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=5, 5, 12) - DAYOFWEEK(CURDATE())) DAY)), + (2, 'day', DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=6, 6, 13) - DAYOFWEEK(CURDATE())) DAY)), + (3, 'day', CURDATE()), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +1 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +2 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +3 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +4 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +5 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +7 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +8 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +9 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +10 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +11 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +12 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +13 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +14 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +15 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +16 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +17 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +18 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +19 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +20 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +21 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +22 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +23 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +24 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +25 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +26 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +27 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +28 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +29 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +30 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +31 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +32 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +33 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +34 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +35 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +36 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +37 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +38 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +39 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +40 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +41 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +42 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +43 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +44 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +45 DAY)), + (3, 'day', DATE_ADD(CURDATE(), INTERVAL +46 DAY)), + (4, 'day', CURDATE()), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +1 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +2 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +3 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +4 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +5 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +7 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +8 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +9 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +10 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +11 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +12 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +13 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +14 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +15 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +16 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +17 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +18 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +19 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +20 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +21 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +22 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +23 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +24 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +25 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +26 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +27 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +28 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +29 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +30 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +31 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +32 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +33 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +34 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +35 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +36 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +37 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +38 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +39 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +40 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +41 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +42 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +43 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +44 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +45 DAY)), + (4, 'day', DATE_ADD(CURDATE(), INTERVAL +46 DAY)), + (5, 'day', CURDATE()), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +1 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +2 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +3 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +4 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +5 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +7 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +8 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +9 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +10 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +11 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +12 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +13 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +14 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +15 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +16 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +17 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +18 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +19 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +20 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +21 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +22 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +23 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +24 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +25 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +26 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +27 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +28 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +29 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +30 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +31 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +32 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +33 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +34 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +35 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +36 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +37 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +38 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +39 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +40 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +41 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +42 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +43 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +44 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +45 DAY)), + (5, 'day', DATE_ADD(CURDATE(), INTERVAL +46 DAY)), + (6, 'day', CURDATE()), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +1 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +2 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +3 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +4 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +5 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +7 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +8 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +9 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +10 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +11 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +12 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +13 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +14 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +15 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +16 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +17 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +18 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +19 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +20 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +21 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +22 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +23 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +24 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +25 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +26 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +27 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +28 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +29 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +30 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +31 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +32 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +33 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +34 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +35 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +36 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +37 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +38 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +39 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +40 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +41 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +42 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +43 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +44 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +45 DAY)), + (6, 'day', DATE_ADD(CURDATE(), INTERVAL +46 DAY)), + (7, 'day', CURDATE()), + (7, 'day', DATE_ADD(CURDATE(), INTERVAL +1 DAY)), + (7, 'day', DATE_ADD(CURDATE(), INTERVAL +2 DAY)), + (7, 'day', DATE_ADD(CURDATE(), INTERVAL +3 DAY)), + (7, 'day', DATE_ADD(CURDATE(), INTERVAL +4 DAY)), + (7, 'day', DATE_ADD(CURDATE(), INTERVAL +5 DAY)), + (7, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY)), + (8, 'day', CURDATE()), + (8, 'day', DATE_ADD(CURDATE(), INTERVAL +1 DAY)), + (8, 'day', DATE_ADD(CURDATE(), INTERVAL +2 DAY)), + (8, 'day', DATE_ADD(CURDATE(), INTERVAL +3 DAY)), + (8, 'day', DATE_ADD(CURDATE(), INTERVAL +4 DAY)), + (8, 'day', DATE_ADD(CURDATE(), INTERVAL +5 DAY)), + (8, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY)); -INSERT INTO `vn`.`workerTimeControl`(`userFk`,`timed`,`manual`) +INSERT INTO `vn`.`workerTimeControl`(`userFk`,`timed`,`manual`, `direction`) VALUES - (106, CONCAT(CURDATE(), ' 07:00'), TRUE), - (106, CONCAT(CURDATE(), ' 10:00'), TRUE), - (106, CONCAT(CURDATE(), ' 10:10'), TRUE), - (106, CONCAT(CURDATE(), ' 15:00'), TRUE); + (106, CONCAT(CURDATE(), ' 07:00'), TRUE, 'in'), + (106, CONCAT(CURDATE(), ' 10:00'), TRUE, 'middle'), + (106, CONCAT(CURDATE(), ' 10:10'), TRUE, 'middle'), + (106, CONCAT(CURDATE(), ' 15:00'), TRUE, 'out'); INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `code`) VALUES @@ -1946,3 +1947,8 @@ INSERT INTO `vn`.`userPhone`(`id`, `userFk`, `typeFk`, `phone`) (65, 107, 'businessPhone', 700987987), (67, 106, 'businessPhone', 1111111112), (68, 106, 'personalPhone', 1111111113); + +INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `weekScope`, `dayWorkMax`, `dayStayMax`) + VALUES + (1, 43200, 129600, 734400, 43200, 50400); + diff --git a/db/tests/vn/buyUltimateFromInterval.spec.js b/db/tests/vn/buyUltimateFromInterval.spec.js index b416b5cb2..b5e6970f7 100644 --- a/db/tests/vn/buyUltimateFromInterval.spec.js +++ b/db/tests/vn/buyUltimateFromInterval.spec.js @@ -5,25 +5,14 @@ describe('buyUltimateFromInterval()', () => { let today; let future; beforeAll(() => { - let date = new Date(); - let month = `${date.getMonth() + 1}`; - let futureMonth = `${date.getMonth() + 2}`; - let day = date.getDate(); - let year = date.getFullYear(); - let futureYear = year; + let now = new Date(); + now.setHours(0, 0, 0, 0); + today = now; - if (month.toString().length < 2) month = '0' + month; - if (futureMonth.toString().length < 2) futureMonth = '0' + futureMonth; - if (futureMonth.toString() == '13') { - futureMonth = '01'; - futureYear + 1; - } - - - if (day.toString().length < 2) day = `0${day}`; - - today = [year, month, day].join('-'); - future = [futureYear, futureMonth, day].join('-'); + let futureDate = new Date(now); + let futureMonth = now.getMonth() + 1; + futureDate.setMonth(futureMonth); + future = futureDate; }); it(`should create a temporal table with it's data`, async() => { @@ -65,8 +54,8 @@ describe('buyUltimateFromInterval()', () => { expect(buyUltimateFromIntervalTable[0].buyFk).toEqual(3); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); - expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); - expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); + expect(buyUltimateFromIntervalTable[0].landed).toEqual(today); + expect(buyUltimateFromIntervalTable[1].landed).toEqual(today); }); it(`should create a temporal table with it's data in which started value is assigned to ended`, async() => { @@ -101,8 +90,8 @@ describe('buyUltimateFromInterval()', () => { expect(buyUltimateFromIntervalTable[0].buyFk).toEqual(3); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); - expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); - expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); + expect(buyUltimateFromIntervalTable[0].landed).toEqual(today); + expect(buyUltimateFromIntervalTable[1].landed).toEqual(today); }); it(`should create a temporal table with it's data in which ended value is a date in the future`, async() => { @@ -137,7 +126,7 @@ describe('buyUltimateFromInterval()', () => { expect(buyUltimateFromIntervalTable[0].buyFk).toEqual(3); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); - expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); - expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); + expect(buyUltimateFromIntervalTable[0].landed).toEqual(today); + expect(buyUltimateFromIntervalTable[1].landed).toEqual(today); }); }); diff --git a/db/tests/vn/orderConfirmWithUser.spec.js b/db/tests/vn/orderConfirmWithUser.spec.js new file mode 100644 index 000000000..f2a3d0c4e --- /dev/null +++ b/db/tests/vn/orderConfirmWithUser.spec.js @@ -0,0 +1,37 @@ +const app = require('vn-loopback/server/server'); +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +// #1885 +xdescribe('order_confirmWithUser()', () => { + it('should confirm an order', async() => { + let stmts = []; + let stmt; + + stmts.push('START TRANSACTION'); + + let params = { + orderFk: 10, + userId: 9 + }; + // problema: la funcion order_confirmWithUser tiene una transacción, por tanto esta nunca hace rollback + stmt = new ParameterizedSQL('CALL hedera.order_confirmWithUser(?, ?)', [ + params.orderFk, + params.userId + ]); + stmts.push(stmt); + + stmt = new ParameterizedSQL('SELECT confirmed FROM hedera.order WHERE id = ?', [ + params.orderFk + ]); + let orderIndex = stmts.push(stmt) - 1; + + stmts.push('ROLLBACK'); + + let sql = ParameterizedSQL.join(stmts, ';'); + let result = await app.models.Ticket.rawStmt(sql); + + savedDescription = result[orderIndex][0].confirmed; + + expect(savedDescription).toBeTruthy(); + }); +}); diff --git a/e2e/helpers/extensions.js b/e2e/helpers/extensions.js index 13aeae436..4d6285603 100644 --- a/e2e/helpers/extensions.js +++ b/e2e/helpers/extensions.js @@ -7,8 +7,6 @@ import config from './config.js'; let currentUser; let actions = { - // Generic extensions - clickIfExists: async function(selector) { let exists = await this.exists(selector); if (exists) await this.click(selector); @@ -25,8 +23,6 @@ let actions = { }, selector); }, - // Salix specific extensions - changeLanguageToEnglish: async function() { let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]'; @@ -49,14 +45,15 @@ let actions = { login: async function(userName) { if (currentUser !== userName) { - let logoutClicked = await this.clickIfExists('#logout'); + let accountClicked = await this.clickIfExists('#user'); - if (logoutClicked) { + if (accountClicked) { let buttonSelector = '.vn-dialog.shown button[response=accept]'; - await this.wait(buttonSelector => { - return document.querySelector(buttonSelector) != null - || location.hash == '#!/login'; - }, buttonSelector); + await this.waitToClick('#logout') + .wait(buttonSelector => { + return document.querySelector(buttonSelector) != null + || location.hash == '#!/login'; + }, buttonSelector); await this.clickIfExists(buttonSelector); } @@ -72,7 +69,7 @@ let actions = { currentUser = userName; } else - await this.waitToClick('vn-topbar a[ui-sref="home"]'); + await this.waitToClick('a[ui-sref=home]'); }, waitForLogin: async function(userName) { @@ -189,7 +186,7 @@ let actions = { let element = selectorMatches[0]; if (selectorMatches.length > 1) - throw new Error(`multiple matches of ${elementSelector} found`); + throw new Error(`Multiple matches of ${elementSelector} found`); let isVisible = false; if (element) { @@ -347,7 +344,7 @@ let actions = { .write('vn-searchbar input', searchValue) .click('vn-searchbar vn-icon[icon="search"]') .wait(100) - .waitForNumberOfElements('.searchResult', 1) + .waitForNumberOfElements('.search-result', 1) .evaluate(() => { return document.querySelector('ui-view vn-card vn-table') != null; }) @@ -362,15 +359,16 @@ let actions = { accessToSection: function(sectionRoute) { return this.wait(`vn-left-menu`) .evaluate(sectionRoute => { - return document.querySelector(`vn-left-menu ul li ul li > a[ui-sref="${sectionRoute}"]`) != null; + return document.querySelector(`vn-left-menu li li > a[ui-sref="${sectionRoute}"]`) != null; }, sectionRoute) .then(nested => { - if (!nested) - return this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); + if (nested) { + this.waitToClick('vn-left-menu vn-item-section > vn-icon[icon=keyboard_arrow_down]') + .wait('vn-left-menu .expanded'); + } - return this.waitToClick('vn-left-menu .collapsed') - .wait('vn-left-menu .expanded') - .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); + return this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`) + .waitForSpinnerLoad(); }); }, @@ -429,10 +427,7 @@ let actions = { }, waitForSpinnerLoad: function() { - return this.wait(() => { - const element = document.querySelector('vn-spinner > div'); - return element.style.display == 'none'; - }); + return this.waitUntilNotPresent('vn-topbar vn-spinner'); } }; diff --git a/e2e/helpers/nightmare.js b/e2e/helpers/nightmare.js index 6706b6dc4..01dcdd5d3 100644 --- a/e2e/helpers/nightmare.js +++ b/e2e/helpers/nightmare.js @@ -13,9 +13,7 @@ module.exports = function createNightmare(width = 1280, height = 720) { x: 0, y: 0, waitTimeout: 2000, - // openDevTools: { - // mode: 'detach' - // } + // openDevTools: {mode: 'detach'} }).viewport(width, height); nightmare.on('console', (type, message, ...args) => { diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 838878b60..7b5b583f6 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -9,7 +9,7 @@ export default { invoiceOutButton: '.modules-menu > li[ui-sref="invoiceOut.index"]', claimsButton: '.modules-menu > li[ui-sref="claim.index"]', returnToModuleIndexButton: 'a[ui-sref="order.index"]', - userMenuButton: 'vn-topbar #user', + userMenuButton: '#user', userLocalWarehouse: '.user-popover vn-autocomplete[ng-model="$ctrl.localWarehouseFk"]', userLocalBank: '.user-popover vn-autocomplete[ng-model="$ctrl.localBankFk"]', userLocalCompany: '.user-popover vn-autocomplete[ng-model="$ctrl.localCompanyFk"]', @@ -23,7 +23,7 @@ export default { clientsIndex: { searchClientInput: `vn-textfield input`, searchButton: 'vn-searchbar vn-icon[icon="search"]', - searchResult: 'vn-client-index .vn-list-item', + searchResult: 'vn-client-index .vn-item', createClientButton: `vn-float-button`, othersButton: 'vn-left-menu li[name="Others"] > a' }, @@ -180,7 +180,7 @@ export default { acceptDeleteButton: '.vn-confirm.shown button[response="accept"]' }, itemsIndex: { - searchIcon: 'vn-item-index vn-searchbar vn-icon[icon="search"]', + searchIcon: 'vn-searchbar vn-icon[icon="search"]', createItemButton: `vn-float-button`, searchResult: 'vn-item-index a.vn-tr', searchResultPreviewButton: 'vn-item-index .buttons > [icon="desktop_windows"]', @@ -221,7 +221,7 @@ export default { moreMenuRegularizeButton: '.vn-drop-down.shown li[name="Regularize stock"]', regularizeQuantityInput: '.vn-dialog.shown tpl-body > div > vn-textfield input', regularizeWarehouseAutocomplete: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.warehouseFk"]', - editButton: 'vn-item-card vn-item-descriptor vn-float-button[icon="edit"]', + editButton: 'vn-item-descriptor vn-float-button[icon="edit"]', regularizeSaveButton: '.vn-dialog.shown tpl-buttons > button', inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]', navigateBackToIndex: 'vn-item-descriptor vn-icon[icon="chevron_left"]' @@ -324,20 +324,20 @@ export default { setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button' }, ticketsIndex: { - openAdvancedSearchButton: 'vn-ticket-index vn-searchbar .append vn-icon[icon="arrow_drop_down"]', + openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]', advancedSearchInvoiceOut: 'vn-ticket-search-panel vn-textfield[ng-model="filter.refFk"] input', newTicketButton: 'vn-ticket-index > a', searchResult: 'vn-ticket-index vn-card > vn-table > div > vn-tbody > a.vn-tr', searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr', searchResultDate: 'vn-ticket-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(5)', - searchTicketInput: `vn-ticket-index vn-textfield input`, - searchWeeklyTicketInput: `vn-ticket-weekly-index vn-textfield input`, - searchWeeklyClearInput: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]', + searchTicketInput: `vn-searchbar input`, + searchWeeklyTicketInput: `vn-searchbar input`, + searchWeeklyClearInput: 'vn-searchbar vn-icon[icon=clear]', advancedSearchButton: 'vn-ticket-search-panel button[type=submit]', - searchButton: 'vn-ticket-index vn-searchbar vn-icon[icon="search"]', - searchWeeklyButton: 'vn-ticket-weekly-index vn-searchbar vn-icon[icon="search"]', + searchButton: 'vn-searchbar vn-icon[icon="search"]', + searchWeeklyButton: 'vn-searchbar vn-icon[icon="search"]', moreMenu: 'vn-ticket-index vn-icon-menu[icon=more_vert]', - moreMenuWeeklyTickets: '.vn-drop-down.shown li:nth-child(2)', + menuWeeklyTickets: 'vn-left-menu [ui-sref="ticket.weekly.index"]', sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6) vn-autocomplete[ng-model="weekly.weekDay"] input', weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr', firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]', @@ -472,7 +472,7 @@ export default { zoneAutocomplete: 'vn-autocomplete[ng-model="$ctrl.zoneId"]', nextStepButton: 'vn-step-control .buttons > section:last-child vn-button', finalizeButton: 'vn-step-control .buttons > section:last-child button[type=submit]', - stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two > form > vn-card > vn-horizontal > table > tfoot > tr > td:nth-child(4)', + stepTwoTotalPriceDif: 'vn-ticket-basic-data-step-two vn-tfoot > vn-tr > :nth-child(6)', chargesReasonAutocomplete: 'vn-autocomplete[ng-model="$ctrl.ticket.option"]', }, ticketComponents: { @@ -516,9 +516,9 @@ export default { saveStateButton: `button[type=submit]` }, claimsIndex: { - searchClaimInput: `vn-claim-index vn-textfield input`, + searchClaimInput: `vn-searchbar input`, searchResult: 'vn-claim-index vn-card > vn-table > div > vn-tbody > a', - searchButton: 'vn-claim-index vn-searchbar vn-icon[icon="search"]' + searchButton: 'vn-searchbar vn-icon[icon="search"]' }, claimDescriptor: { moreMenu: 'vn-claim-descriptor vn-icon-menu[icon=more_vert]', @@ -584,7 +584,7 @@ export default { searchResultDate: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(4)', searchResultAddress: 'vn-order-index vn-table vn-tbody > a:nth-child(1) > vn-td:nth-child(6)', searchOrderInput: `vn-order-index vn-textfield input`, - searchButton: 'vn-order-index vn-searchbar vn-icon[icon="search"]', + searchButton: 'vn-searchbar vn-icon[icon="search"]', createOrderButton: `vn-float-button`, }, orderDescriptor: { @@ -679,34 +679,35 @@ export default { saturdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(6) > vn-icon-button', sundayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(7) > vn-icon-button', confirmButton: '.vn-dialog.shown tpl-buttons > button', - firstEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > span', - firstEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > span', - firstEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > span', - firstEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > span', - firstEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > span', - firstEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > span', - firstEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > span', - secondEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > span', - secondEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > span', - secondEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > span', - secondEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > span', - secondEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > span', - secondEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > span', - secondEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > span', - thirdEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > span', - thirdEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > span', - thirdEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > span', - thirdEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > span', - thirdEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > span', - thirdEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > span', - thirdEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > span', - fourthEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > span', - fourthEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > span', - fourthEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > span', - fourthEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > span', - fourthEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > span', - fourthEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > span', - fourthEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > span', + firstEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > vn-chip > div', + firstEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > vn-chip > div', + firstEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > vn-chip > div', + firstEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > vn-chip > div', + firstEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > vn-chip > div', + firstEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > vn-chip > div', + firstEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > vn-chip > div', + secondEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > vn-chip > div', + secondEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > vn-chip > div', + secondEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > vn-chip > div', + secondEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > vn-chip > div', + secondEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > vn-chip > div', + secondEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > vn-chip > div', + secondEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > vn-chip > div', + thirdEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > vn-chip > div', + thirdEntryOfMondayDelete: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > vn-chip > vn-icon[icon="cancel"]', + thirdEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > vn-chip > div', + thirdEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > vn-chip > div', + thirdEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > vn-chip > div', + thirdEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > vn-chip > div', + thirdEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > vn-chip > div', + thirdEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > vn-chip > div', + fourthEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > vn-chip > div', + fourthEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > vn-chip > div', + fourthEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > vn-chip > div', + fourthEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > vn-chip > div', + fourthEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > vn-chip > div', + fourthEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > vn-chip > div', + fourthEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > vn-chip > div', mondayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(1)', tuesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(2)', wednesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(3)', @@ -717,11 +718,12 @@ export default { weekWorkedHours: 'vn-worker-time-control vn-side-menu vn-label-value > section > span', nextMonthButton: 'vn-worker-time-control vn-side-menu vn-calendar vn-button[icon=keyboard_arrow_right]', secondWeekDay: 'vn-worker-time-control vn-side-menu vn-calendar .day:nth-child(8) > .day-number', - navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]' + navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]', + acceptDeleteDialog: '.vn-confirm.shown button[response="accept"]' }, invoiceOutIndex: { - searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`, - searchButton: 'vn-invoice-out-index vn-searchbar vn-icon[icon="search"]', + searchInvoiceOutInput: `vn-searchbar input`, + searchButton: 'vn-searchbar vn-icon[icon="search"]', searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr', }, invoiceOutDescriptor: { diff --git a/e2e/paths/01-login/01_login.spec.js b/e2e/paths/01-login/01_login.spec.js index 03acc98a8..f612ad23c 100644 --- a/e2e/paths/01-login/01_login.spec.js +++ b/e2e/paths/01-login/01_login.spec.js @@ -30,7 +30,7 @@ describe('Login path', () => { it('should log in', async() => { const url = await nightmare .doLogin('employee', null) - .wait('#logout') + .wait('#user') .parsedUrl(); expect(url.hash).toEqual('#!/'); diff --git a/e2e/paths/03-worker-module/02_time_control.spec.js b/e2e/paths/03-worker-module/02_time_control.spec.js index ce7316773..706fc2a74 100644 --- a/e2e/paths/03-worker-module/02_time_control.spec.js +++ b/e2e/paths/03-worker-module/02_time_control.spec.js @@ -35,8 +35,30 @@ describe('Worker time control path', () => { expect(result).toEqual(scanTime); }); - it(`should scan out Hank Pym and forget to scan in from the break`, async() => { - const scanTime = '15:00'; + it(`should scan in Hank Pym for a wrong hour and forget to scan in from the break`, async() => { + const scanTime = '18:00'; + const result = await nightmare + .waitToClick(selectors.workerTimeControl.mondayAddTimeButton) + .pickTime(selectors.workerTimeControl.timeDialogInput, scanTime) + .waitToClick(selectors.workerTimeControl.confirmButton) + .waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText'); + + expect(result).toEqual(scanTime); + }); + + it(`should delete the wrong entry for Hank Pym`, async() => { + const wrongScanTime = '18:00'; + const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.thirdEntryOfMonday, wrongScanTime) + .waitToClick(selectors.workerTimeControl.thirdEntryOfMondayDelete) + .waitToClick(selectors.workerTimeControl.acceptDeleteDialog) + .waitForLastSnackbar(); + + expect(result).toEqual('Entry removed'); + }); + + it(`should scan out Hank Pym to leave early`, async() => { + const scanTime = '14:00'; const result = await nightmare .waitToClick(selectors.workerTimeControl.mondayAddTimeButton) .pickTime(selectors.workerTimeControl.timeDialogInput, scanTime) @@ -54,7 +76,7 @@ describe('Worker time control path', () => { .waitToClick(selectors.workerTimeControl.confirmButton) .waitToGetProperty(selectors.workerTimeControl.fourthEntryOfMonday, 'innerText'); - expect(result).toEqual('15:00'); + expect(result).toEqual('14:00'); }); it(`should the third entry be the scan in from break`, async() => { @@ -67,9 +89,10 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 hours`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '07:00 h.') .waitToGetProperty(selectors.workerTimeControl.mondayWorkedHours, 'innerText'); - expect(result).toEqual('08:00 h.'); + expect(result).toEqual('07:00 h.'); }); }); @@ -120,6 +143,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 happy hours`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.tuesdayWorkedHours, '08:00 h.') .waitToGetProperty(selectors.workerTimeControl.tuesdayWorkedHours, 'innerText'); expect(result).toEqual('08:00 h.'); @@ -173,6 +197,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 cheerfull hours`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.wednesdayWorkedHours, '08:00 h.') .waitToGetProperty(selectors.workerTimeControl.wednesdayWorkedHours, 'innerText'); expect(result).toEqual('08:00 h.'); @@ -226,6 +251,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 joyfull hours`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.thursdayWorkedHours, '08:00 h.') .waitToGetProperty(selectors.workerTimeControl.thursdayWorkedHours, 'innerText'); expect(result).toEqual('08:00 h.'); @@ -279,6 +305,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 hours with a smile on his face`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.fridayWorkedHours, '08:00 h.') .waitToGetProperty(selectors.workerTimeControl.fridayWorkedHours, 'innerText'); expect(result).toEqual('08:00 h.'); @@ -319,6 +346,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 hours with all his will`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.saturdayWorkedHours, '08:00 h.') .waitToGetProperty(selectors.workerTimeControl.saturdayWorkedHours, 'innerText'); expect(result).toEqual('08:00 h.'); @@ -350,6 +378,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym worked 8 glad hours`, async() => { const result = await nightmare + .waitForTextInElement(selectors.workerTimeControl.sundayWorkedHours, '08:00 h.') .waitToGetProperty(selectors.workerTimeControl.sundayWorkedHours, 'innerText'); expect(result).toEqual('08:00 h.'); @@ -387,10 +416,10 @@ describe('Worker time control path', () => { it('should Hank Pym check his hours are alright', async() => { const wholeWeekHours = await nightmare - .waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '56:00 h.') + .waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '55:00 h.') .waitToGetProperty(selectors.workerTimeControl.weekWorkedHours, 'innerText'); - expect(wholeWeekHours).toEqual('56:00 h.'); + expect(wholeWeekHours).toEqual('55:00 h.'); }); }); }); diff --git a/e2e/paths/04-item-module/01_summary.spec.js b/e2e/paths/04-item-module/01_summary.spec.js index bd06ebef4..8f493bfac 100644 --- a/e2e/paths/04-item-module/01_summary.spec.js +++ b/e2e/paths/04-item-module/01_summary.spec.js @@ -80,7 +80,7 @@ describe('Item summary path', () => { it('should search for other item', async() => { const result = await nightmare - .clearInput('vn-item-index vn-searchbar input') + .clearInput('vn-searchbar input') .waitToClick(selectors.itemsIndex.searchButton) .write(selectors.itemsIndex.searchItemInput, 'Melee weapon combat fist 15cm') .waitToClick(selectors.itemsIndex.searchButton) diff --git a/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js b/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js index 8e4645051..ec9b16fa4 100644 --- a/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js +++ b/e2e/paths/05-ticket-module/02_expeditions_and_log.spec.js @@ -7,7 +7,7 @@ describe('Ticket expeditions and log path', () => { beforeAll(() => { return nightmare .loginAndModule('production', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.expedition'); }); diff --git a/e2e/paths/05-ticket-module/04_packages.spec.js b/e2e/paths/05-ticket-module/04_packages.spec.js index 30324aec2..b51bb8b95 100644 --- a/e2e/paths/05-ticket-module/04_packages.spec.js +++ b/e2e/paths/05-ticket-module/04_packages.spec.js @@ -7,7 +7,7 @@ describe('Ticket Create packages path', () => { beforeAll(() => { return nightmare .loginAndModule('employee', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.package'); }); diff --git a/e2e/paths/05-ticket-module/05_tracking_state.spec.js b/e2e/paths/05-ticket-module/05_tracking_state.spec.js index 5797b6798..7c319f1c9 100644 --- a/e2e/paths/05-ticket-module/05_tracking_state.spec.js +++ b/e2e/paths/05-ticket-module/05_tracking_state.spec.js @@ -8,7 +8,7 @@ describe('Ticket Create new tracking state path', () => { beforeAll(() => { return nightmare .loginAndModule('production', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.tracking.index'); }); @@ -44,7 +44,7 @@ describe('Ticket Create new tracking state path', () => { beforeAll(() => { return nightmare .loginAndModule('salesPerson', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.tracking.index'); }); diff --git a/e2e/paths/05-ticket-module/08_components.spec.js b/e2e/paths/05-ticket-module/08_components.spec.js index 4603650ac..501c2eaad 100644 --- a/e2e/paths/05-ticket-module/08_components.spec.js +++ b/e2e/paths/05-ticket-module/08_components.spec.js @@ -7,7 +7,7 @@ describe('Ticket List components path', () => { beforeAll(() => { return nightmare .loginAndModule('employee', 'ticket') - .accessToSearchResult('id:1') + .accessToSearchResult('1') .accessToSection('ticket.card.components'); }); diff --git a/e2e/paths/05-ticket-module/09_weekly.spec.js b/e2e/paths/05-ticket-module/09_weekly.spec.js index a7a301d79..ad8a8020c 100644 --- a/e2e/paths/05-ticket-module/09_weekly.spec.js +++ b/e2e/paths/05-ticket-module/09_weekly.spec.js @@ -11,8 +11,7 @@ describe('Ticket descriptor path', () => { it('should count the amount of tickets in the turns section', async() => { const result = await nightmare - .waitToClick(selectors.ticketsIndex.moreMenu) - .waitToClick(selectors.ticketsIndex.moreMenuWeeklyTickets) + .waitToClick(selectors.ticketsIndex.menuWeeklyTickets) .wait(selectors.ticketsIndex.weeklyTicket) .countElement(selectors.ticketsIndex.weeklyTicket); @@ -72,8 +71,7 @@ describe('Ticket descriptor path', () => { it('should confirm the ticket 11 was added on thursday', async() => { const result = await nightmare - .waitToClick(selectors.ticketsIndex.moreMenu) - .waitToClick(selectors.ticketsIndex.moreMenuWeeklyTickets) + .waitToClick(selectors.ticketsIndex.menuWeeklyTickets) .waitToGetProperty(selectors.ticketsIndex.sixthWeeklyTicket, 'value'); expect(result).toEqual('Thursday'); @@ -132,8 +130,7 @@ describe('Ticket descriptor path', () => { it('should confirm the ticket 11 was added on saturday', async() => { const result = await nightmare - .waitToClick(selectors.ticketsIndex.moreMenu) - .waitToClick(selectors.ticketsIndex.moreMenuWeeklyTickets) + .waitToClick(selectors.ticketsIndex.menuWeeklyTickets) .waitToGetProperty(selectors.ticketsIndex.sixthWeeklyTicket, 'value'); expect(result).toEqual('Saturday'); @@ -160,7 +157,7 @@ describe('Ticket descriptor path', () => { it('should confirm the sixth weekly ticket was deleted', async() => { const result = await nightmare - .waitToClick('vn-ticket-weekly-index vn-searchbar vn-icon[icon=clear]') + .waitToClick('vn-searchbar vn-icon[icon=clear]') .waitToClick(selectors.ticketsIndex.searchWeeklyButton) .waitForNumberOfElements(selectors.ticketsIndex.searchWeeklyResult, 5) .countElement(selectors.ticketsIndex.searchWeeklyResult); diff --git a/e2e/paths/05-ticket-module/10_request.spec.js b/e2e/paths/05-ticket-module/10_request.spec.js index 0a31cb75a..44dc3e1af 100644 --- a/e2e/paths/05-ticket-module/10_request.spec.js +++ b/e2e/paths/05-ticket-module/10_request.spec.js @@ -7,7 +7,7 @@ describe('Ticket purchase request path', () => { beforeAll(() => { nightmare .loginAndModule('salesPerson', 'ticket') - .accessToSearchResult('id:16') + .accessToSearchResult('16') .accessToSection('ticket.card.request.index'); }); diff --git a/e2e/paths/05-ticket-module/11_diary.spec.js b/e2e/paths/05-ticket-module/11_diary.spec.js index aeec780e7..c64c0ba7b 100644 --- a/e2e/paths/05-ticket-module/11_diary.spec.js +++ b/e2e/paths/05-ticket-module/11_diary.spec.js @@ -1,5 +1,4 @@ import selectors from '../../helpers/selectors.js'; -import config from '../../helpers/config.js'; import createNightmare from '../../helpers/nightmare'; describe('Ticket diary path', () => { @@ -31,12 +30,9 @@ describe('Ticket diary path', () => { }); it(`should navigate to the item diary from the 1st sale item id descriptor popover`, async() => { - const itemId = 2; const url = await nightmare .waitToClick(selectors.ticketSummary.firstSaleItemId) .waitToClick(selectors.ticketSummary.popoverDiaryButton) - .waitForLogin('employee') - .goto(`${config.url}#!/item/${itemId}/diary?warehouseFk=1&ticketFk=1`) .parsedUrl(); expect(url.hash).toContain('/diary'); diff --git a/e2e/paths/05-ticket-module/12_descriptor.spec.js b/e2e/paths/05-ticket-module/12_descriptor.spec.js index 81bb75bdb..9442f1be0 100644 --- a/e2e/paths/05-ticket-module/12_descriptor.spec.js +++ b/e2e/paths/05-ticket-module/12_descriptor.spec.js @@ -9,8 +9,7 @@ describe('Ticket descriptor path', () => { .loginAndModule('salesperson', 'ticket'); }); - // Excluded waiting for #1874 - xdescribe('Delete ticket', () => { + describe('Delete ticket', () => { it('should search for an specific ticket', async() => { const result = await nightmare .write(selectors.ticketsIndex.searchTicketInput, 18) diff --git a/e2e/paths/05-ticket-module/13_services.spec.js b/e2e/paths/05-ticket-module/13_services.spec.js index 775357bda..01da91f1b 100644 --- a/e2e/paths/05-ticket-module/13_services.spec.js +++ b/e2e/paths/05-ticket-module/13_services.spec.js @@ -21,7 +21,7 @@ describe('Ticket services path', () => { .isDisabled(selectors.ticketService.firstAddDescriptionButton); expect(result).toBeTruthy(); - }); + }, 100000); it('should receive an error if you attempt to save a service without access rights', async() => { const result = await nightmare diff --git a/front/core/components/autocomplete/index.js b/front/core/components/autocomplete/index.js index 9808fd667..e2c9b7ad4 100755 --- a/front/core/components/autocomplete/index.js +++ b/front/core/components/autocomplete/index.js @@ -208,11 +208,9 @@ export default class Autocomplete extends Field { onContainerKeyDown(event) { if (event.defaultPrevented) return; - switch (event.key) { case 'ArrowUp': case 'ArrowDown': - case 'Enter': this.showDropDown(); break; default: diff --git a/front/core/components/button-menu/index.js b/front/core/components/button-menu/index.js index 946e1fd7f..c19962b08 100644 --- a/front/core/components/button-menu/index.js +++ b/front/core/components/button-menu/index.js @@ -7,7 +7,7 @@ export default class ButtonMenu extends Button { constructor($element, $scope, $transclude) { super($element, $scope); this.$transclude = $transclude; - $element.on('click', e => this.onClick(e)); + $element.on('click', e => this.onButtonClick(e)); } get model() { @@ -41,8 +41,7 @@ export default class ButtonMenu extends Button { Object.assign(this.$.dropDown, props); } - onClick(event) { - if (this.disabled) return; + onButtonClick(event) { if (event.defaultPrevented) return; this.emit('open'); this.showDropDown(); diff --git a/front/core/components/button-menu/index.spec.js b/front/core/components/button-menu/index.spec.js index 1e8c048ff..87f677001 100644 --- a/front/core/components/button-menu/index.spec.js +++ b/front/core/components/button-menu/index.spec.js @@ -14,7 +14,7 @@ describe('Component vnButtonMenu', () => { $element.remove(); }); - describe('onClick(event)', () => { + describe('onButtonClick(event)', () => { it(`should emit the open event`, () => { spyOn(controller, 'emit'); @@ -23,7 +23,7 @@ describe('Component vnButtonMenu', () => { bubbles: true, cancelable: true }); - controller.onClick(event); + controller.onButtonClick(event); expect(controller.emit).toHaveBeenCalledWith('open'); }); diff --git a/front/core/components/button/style.scss b/front/core/components/button/style.scss index 74dcbcf9b..032c88cbb 100644 --- a/front/core/components/button/style.scss +++ b/front/core/components/button/style.scss @@ -39,19 +39,19 @@ } &.colored { color: white; - background-color: $color-main; + background-color: $color-button; box-shadow: 0 .15em .15em 0 rgba(0, 0, 0, .3); transition: background 200ms ease-in-out; &:not(.disabled) { &:hover, &:focus { - background-color: lighten($color-main, 10%); + background-color: lighten($color-button, 10%); } } } &.flat { - color: $color-main; + color: $color-button; background-color: transparent; box-shadow: none; transition: background 200ms ease-in-out; diff --git a/front/core/components/check/style.scss b/front/core/components/check/style.scss index 31715a2cd..b84b61ce5 100644 --- a/front/core/components/check/style.scss +++ b/front/core/components/check/style.scss @@ -19,7 +19,7 @@ } &.checked > .btn { border-color: transparent; - background-color: $color-main; + background-color: $color-button; & > .mark { top: 0; diff --git a/front/core/components/chip/style.scss b/front/core/components/chip/style.scss index 4476d089e..5840ecdf1 100644 --- a/front/core/components/chip/style.scss +++ b/front/core/components/chip/style.scss @@ -24,13 +24,12 @@ vn-chip { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - line-height: 2em; & > vn-avatar { margin-left: -0.7em; margin-right: .3em; vertical-align: middle; - height: 1.9em; + height: 2em; width: 2em; } } diff --git a/front/core/components/date-picker/index.js b/front/core/components/date-picker/index.js index 17ce19406..24f8cf90f 100644 --- a/front/core/components/date-picker/index.js +++ b/front/core/components/date-picker/index.js @@ -14,7 +14,9 @@ class DatePicker extends Field { let value = this.input.value; if (value) { - date = new Date(value); + let ymd = value.split('-') + .map(e => parseInt(e)); + date = new Date(ymd[0], ymd[1] - 1, ymd[2]); if (this.field) { let orgDate = this.field instanceof Date diff --git a/front/core/components/date-picker/index.spec.js b/front/core/components/date-picker/index.spec.js index 411a92105..2dd419e07 100644 --- a/front/core/components/date-picker/index.spec.js +++ b/front/core/components/date-picker/index.spec.js @@ -3,6 +3,10 @@ describe('Component vnDatePicker', () => { let $element; let $ctrl; + let today; + today = new Date(); + today.setHours(0, 0, 0, 0); + beforeEach(ngModule('vnCore')); beforeEach(angular.mock.inject(($compile, $rootScope, _$filter_) => { @@ -18,14 +22,19 @@ describe('Component vnDatePicker', () => { describe('field() setter', () => { it(`should display the formated the date`, () => { - let today; - today = new Date(); - today.setHours(0, 0, 0, 0); - $ctrl.field = today; let displayed = $filter('date')(today, 'yyyy-MM-dd'); expect($ctrl.value).toEqual(displayed); }); }); + + describe('onValueUpdate()', () => { + it(`should change the picker value to selected date`, () => { + $ctrl.value = $filter('date')(today, 'yyyy-MM-dd'); + $ctrl.input.dispatchEvent(new Event('change')); + + expect($ctrl.field).toEqual(today); + }); + }); }); diff --git a/front/core/components/dialog/index.js b/front/core/components/dialog/index.js index 0891542a2..7a7cb5f9f 100644 --- a/front/core/components/dialog/index.js +++ b/front/core/components/dialog/index.js @@ -29,6 +29,10 @@ export default class Dialog extends Popup { * @return {Promise} A promise that will be resolved with response when dialog is closed */ show(data, responseHandler) { + if (this.shown) + return this.$q.reject(new Error('Dialog already shown')); + super.show(); + if (typeof data == 'function') { responseHandler = data; data = null; @@ -36,27 +40,27 @@ export default class Dialog extends Popup { this.data = data; this.showHandler = responseHandler; - super.show(); return this.$q(resolve => { this.resolve = resolve; }); } /** - * Hides the dialog. + * Hides the dialog resolving the promise returned by show(). * * @param {String} response The response */ hide(response) { if (!this.shown) return; - this.showHandler = null; super.hide(); + + this.showHandler = null; if (this.resolve) this.resolve(response); } /** - * Calls the response handler. + * Calls the response handlers. * * @param {String} response The response code * @return {Boolean} The response handler return diff --git a/front/core/components/dialog/index.spec.js b/front/core/components/dialog/index.spec.js index a898261fb..8c41bc060 100644 --- a/front/core/components/dialog/index.spec.js +++ b/front/core/components/dialog/index.spec.js @@ -28,14 +28,6 @@ describe('Component vnDialog', () => { expect(called).toBeTruthy(); }); - it(`should hide the dialog when response is given`, () => { - controller.show(); - spyOn(controller, 'hide'); - controller.respond('answer'); - - expect(controller.hide).toHaveBeenCalledWith('answer'); - }); - it(`should not hide the dialog when false is returned from response handler`, () => { controller.show(() => false); spyOn(controller, 'hide'); @@ -46,12 +38,13 @@ describe('Component vnDialog', () => { }); describe('hide()', () => { - it(`should do nothing if it's already hidden`, () => { - controller.onResponse = () => {}; - spyOn(controller, 'onResponse'); + it(`should resolve the promise returned by show`, () => { + let resolved = true; + controller.show().then(() => resolved = true); controller.hide(); + $scope.$apply(); - expect(controller.onResponse).not.toHaveBeenCalledWith(); + expect(resolved).toBeTruthy(); }); }); @@ -94,7 +87,7 @@ describe('Component vnDialog', () => { expect(controller.onAccept).toHaveBeenCalledWith({$response: 'accept'}); }); - it(`should resolve the promise returned by show() with response when hidden`, () => { + it(`should resolve the promise returned by show() with response`, () => { let response; controller.show().then(res => response = res); controller.respond('response'); diff --git a/front/core/components/dialog/style.scss b/front/core/components/dialog/style.scss index 9461a0063..2f3d9a028 100644 --- a/front/core/components/dialog/style.scss +++ b/front/core/components/dialog/style.scss @@ -36,7 +36,7 @@ background-color: transparent; border: none; border-radius: .1em; - color: $color-main; + color: $color-button; font-family: vn-font-bold; padding: .7em; margin: -0.7em; diff --git a/front/core/components/drop-down/index.js b/front/core/components/drop-down/index.js index 163553aeb..9f2dfe424 100644 --- a/front/core/components/drop-down/index.js +++ b/front/core/components/drop-down/index.js @@ -5,6 +5,7 @@ import template from './index.html'; import ArrayModel from '../array-model/array-model'; import CrudModel from '../crud-model/crud-model'; import {mergeWhere} from 'vn-loopback/util/filter'; +import focus from '../../lib/focus'; /** * @event select Thrown when model item is selected @@ -86,9 +87,11 @@ export default class DropDown extends Popover { * @param {String} search The initial search term or %null */ show(parent, search) { - this._activeOption = -1; + if (this.shown) return; super.show(parent); + this._activeOption = -1; + this.list = this.popup.querySelector('.list'); this.ul = this.popup.querySelector('ul'); @@ -102,21 +105,25 @@ export default class DropDown extends Popover { this.search = search; this.buildList(); - let input = this.popup.querySelector('input'); - setTimeout(() => input.focus()); + focus(this.popup.querySelector('input')); } - onClose() { + hide() { + if (!this.shown) return; + super.hide(); + this.document.removeEventListener('keydown', this.docKeyDownHandler); this.docKeyDownHandler = null; this.list.removeEventListener('scroll', this.listScrollHandler); this.listScrollHandler = null; + } + onClose() { + this.destroyList(); this.list = null; this.ul = null; - this.destroyList(); super.onClose(); } @@ -387,7 +394,7 @@ export default class DropDown extends Popover { let filter = { order, - limit: this.limit || 8 + limit: this.limit || 30 }; if (model instanceof CrudModel) { diff --git a/front/core/components/field/index.html b/front/core/components/field/index.html index 8d12ddd77..d614d157f 100644 --- a/front/core/components/field/index.html +++ b/front/core/components/field/index.html @@ -28,8 +28,7 @@ ng-transclude="append" class="append"> -
-
+
diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js index 0155c88a8..481ccce91 100644 --- a/front/core/components/field/index.js +++ b/front/core/components/field/index.js @@ -50,7 +50,7 @@ export default class Field extends FormInput { } set value(value) { - this.field = value; + this.input.value = value; } get value() { diff --git a/front/core/components/field/style.scss b/front/core/components/field/style.scss index add3a3228..f3bc0ae04 100644 --- a/front/core/components/field/style.scss +++ b/front/core/components/field/style.scss @@ -2,6 +2,7 @@ .vn-field { display: inline-block; + box-sizing: border-box; width: 100%; & > .container { @@ -22,7 +23,7 @@ top: 18px; line-height: 20px; pointer-events: none; - color: $color-font-secondary; + color: $color-font-bg-marginal; transition-property: top, color, font-size; transition-duration: 400ms; transition-timing-function: cubic-bezier(.4, 0, .2, 1); @@ -67,7 +68,11 @@ } & > input { position: relative; + color: $color-font; + &::placeholder { + color: $color-font-bg; + } &[type=time], &[type=date], &[type=password] { @@ -121,12 +126,13 @@ & > .icons { display: flex; align-items: center; - color: $color-font-secondary; + color: $color-font-bg-marginal; } & > .prepend > prepend, & > .append > append, & > .icons { display: flex; + align-items: center; & > vn-icon { font-size: 24px; @@ -159,7 +165,7 @@ } &.focus { height: 2px; - background-color: $color-main; + background-color: $color-primary; left: 50%; width: 0; transition-property: width, left, background-color; @@ -190,13 +196,57 @@ } } } + &.standout { + border-radius: .1em; + background-color: rgba(255, 255, 255, .1); + padding: 0 12px; + transition-property: background-color, color; + transition-duration: 200ms; + transition-timing-function: ease-in-out; + + & > .container { + & > .underline { + display: none; + } + & > .infix > .control > * { + color: $color-font-dark; + + &::placeholder { + color: $color-font-bg-dark; + } + } + & > .prepend, + & > .append, + & > .icons { + color: $color-font-bg-dark-marginal; + } + } + &.focused { + background-color: $color-bg-panel; + + & > .container { + & > .infix > .control > * { + color: $color-font; + + &::placeholder { + color: $color-font-bg; + } + } + & > .prepend, + & > .append, + & > .icons { + color: $color-font-bg-marginal; + } + } + } + } &.not-empty, &.focused, &.invalid { & > .container > .infix { & > label { top: 5px; - color: $color-main; + color: $color-primary; padding: 0; font-size: 12px; } diff --git a/front/core/components/icon-button/style.scss b/front/core/components/icon-button/style.scss index 1126c5e6d..d59980a62 100644 --- a/front/core/components/icon-button/style.scss +++ b/front/core/components/icon-button/style.scss @@ -2,7 +2,7 @@ .vn-icon-button { @extend %clickable-light; - color: $color-main; + color: $color-button; & > button { padding: .2em !important; diff --git a/front/core/components/index.js b/front/core/components/index.js index bc19db642..0ebd03420 100644 --- a/front/core/components/index.js +++ b/front/core/components/index.js @@ -41,6 +41,7 @@ import './list'; import './popover'; import './popup'; import './radio'; +import './slot'; import './submit'; import './table'; import './td-editable'; diff --git a/front/core/components/list/style.scss b/front/core/components/list/style.scss index 0786cacf8..6f12ce7c6 100644 --- a/front/core/components/list/style.scss +++ b/front/core/components/list/style.scss @@ -1,45 +1,102 @@ @import "./effects"; +/* +ul.menu { + list-style-type: none; + padding: 0; + padding-top: $spacing-md; + margin: 0; + font-size: inherit; + & > li > a { + @extend %clickable; + display: block; + color: inherit; + padding: .6em 2em; + } +} +*/ + +vn-list, .vn-list { + display: block; max-width: $width-sm; margin: 0 auto; + padding: 0; + list-style-type: none; - a.vn-list-item { - @extend %clickable; + vn-list, + .vn-list { + vn-item, + .vn-item { + padding-left: $spacing-lg; + } } - .vn-list-item { - border-bottom: $border-thin-light; - display: block; - text-decoration: none; - color: inherit; - - & > vn-horizontal { + &.separated { + vn-item, + .vn-item { + border-bottom: $border-thin-light; padding: $spacing-md; - - & > vn-one { - overflow: hidden; + + &:last-child { + border-bottom: none; } - & > .buttons { - align-items: center; + } + } +} + +vn-item, +.vn-item { + @extend %clickable; + display: flex; + align-items: center; + color: inherit; + padding: $spacing-sm $spacing-md; + text-decoration: none; + min-height: 40px; + box-sizing: border-box; + + &.separated { + border-bottom: $border-thin-light; - vn-icon-button { - opacity: .4; - margin-left: .5em; - transition: opacity 250ms ease-out; - padding: 0; - font-size: 1.2em; - - &:hover { - opacity: 1; - } + &:last-child { + border-bottom: none; + } + } + &.active { + @extend %active; + } + & > vn-item-section { + overflow: hidden; + flex: 1; + + &[avatar] { + display: flex; + flex: none; + align-items: center; + margin-right: $spacing-md; + + & > .vn-icon { + font-size: 1.2em; + } + } + &[side] { + display: flex; + flex: none; + align-items: center; + + & > .vn-button { + opacity: .4; + margin-left: .5em; + transition: opacity 250ms ease-out; + padding: 0; + font-size: 1.05em; + + &:hover { + opacity: 1; } } } } - vn-empty-rows { - display: block; - text-align: center; - padding: 1.5em; - box-sizing: border-box; - } -} \ No newline at end of file +} + + diff --git a/front/core/components/popup/index.js b/front/core/components/popup/index.js index 525346819..994f4a0e7 100644 --- a/front/core/components/popup/index.js +++ b/front/core/components/popup/index.js @@ -98,8 +98,9 @@ export default class Popup extends Component { onClose() { this.closeTimeout = null; this.popup.remove(); - this.$contentScope.$destroy(); this.popup = null; + this.$contentScope.$destroy(); + this.$contentScope = null; this.windowEl = null; this.emit('close'); } diff --git a/front/core/components/radio/style.scss b/front/core/components/radio/style.scss index 787c0986e..2ee037e65 100644 --- a/front/core/components/radio/style.scss +++ b/front/core/components/radio/style.scss @@ -9,7 +9,7 @@ } } &.checked > .btn { - border-color: $color-main; + border-color: $color-button; & > .mark { position: absolute; @@ -19,7 +19,7 @@ transform: translate(-50%, -50%); width: 10px; height: 10px; - background-color: $color-main; + background-color: $color-button; } } &.disabled.checked > .btn > .mark { diff --git a/front/core/components/range/style.scss b/front/core/components/range/style.scss index a370b4c39..6898f8cda 100644 --- a/front/core/components/range/style.scss +++ b/front/core/components/range/style.scss @@ -5,7 +5,7 @@ -webkit-appearance: none; margin-top: -5px; border-radius: 50%; - background: $color-main; + background: $color-button; border: none; height: 12px; width: 12px; @@ -15,7 +15,7 @@ transition-timing-function: ease-out; } &:focus::#{$thumb-selector} { - box-shadow: 0 0 0 10px rgba($color-main, .2); + box-shadow: 0 0 0 10px rgba($color-button, .2); } &:active::#{$thumb-selector} { transform: scale(1.5); @@ -29,7 +29,7 @@ width: 100%; height: 3px; cursor: inherit; - background: $color-secondary; + background: $color-marginal; border-radius: 2px; border: none; } @@ -40,7 +40,7 @@ font-size: 12px; &.main { - color: $color-main; + color: $color-button; } &.min-label { float: left; diff --git a/front/core/components/searchbar/__snapshots__/searchbar.spec.js.snap b/front/core/components/searchbar/__snapshots__/searchbar.spec.js.snap deleted file mode 100644 index 698e1ba88..000000000 --- a/front/core/components/searchbar/__snapshots__/searchbar.spec.js.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Component vnSearchbar doSearch() should define searchString, call onSearch and the model applyFilter method 1`] = `"function search() { [native code] } 0:i 1:d 2:: 3:( ) 4:1 5:2 6:3 7:4 8:5 9:6"`; - -exports[`Component vnSearchbar onPanelSubmit() should hide the popover, and set the filter as it wasn't defined 1`] = `Object {}`; - -exports[`Component vnSearchbar onPanelSubmit() should hide the popover, compact the filter 1`] = ` -Object { - "id": 999, -} -`; - -exports[`Component vnSearchbar onSubmit() should define the controller's filter 1`] = ` -Object { - "search": "id: 999", -} -`; diff --git a/front/core/components/searchbar/searchbar.html b/front/core/components/searchbar/searchbar.html index 069a9411c..d6f4720f2 100644 --- a/front/core/components/searchbar/searchbar.html +++ b/front/core/components/searchbar/searchbar.html @@ -1,14 +1,26 @@
+
+ + + + {{param.chip}} + +
this.onStateChange()); + constructor($element, $) { + super($element, $); + this.searchState = '.'; - this._filter = null; - this.autoLoad = false; + let criteria = {}; + this.deregisterCallback = this.$transitions.onSuccess( + criteria, () => this.onStateChange()); } $postLink() { - if (this.filter === null) - this.onStateChange(); + this.onStateChange(); } - set filter(value) { - this._filter = value; - this.$state.go('.', {q: JSON.stringify(value)}, {location: 'replace'}); + $onDestroy() { + this.deregisterCallback(); } get filter() { return this._filter; } - onStateChange() { - this._filter = null; - - if (this.$state.params.q) { - try { - this._filter = JSON.parse(this.$state.params.q); - } catch (e) { - console.error(e); - } - } - - this.doSearch(); + set filter(value) { + this._filter = value; + this.toBar(value); } get shownFilter() { - return this._filter != null ? this._filter : this.suggestedFilter; + return this.filter != null + ? this.filter + : this.suggestedFilter; + } + + get searchString() { + return this._searchString; + } + + set searchString(value) { + this._searchString = value; + if (value == null) this.params = []; + } + + onStateChange() { + let filter = null; + + if (this.$state.is(this.searchState)) { + if (this.$params.q) { + try { + filter = JSON.parse(this.$params.q); + } catch (e) { + console.error(e); + } + } + + focus(this.element.querySelector('vn-textfield input')); + } + + this.filter = filter; } openPanel(event) { @@ -88,21 +99,136 @@ export default class Controller extends Component { onPanelSubmit(filter) { this.$.popover.hide(); filter = compact(filter); - this.filter = filter != null ? filter : {}; - - let element = this.element.querySelector('vn-textfield input'); - element.select(); - element.focus(); + filter = filter != null ? filter : {}; + this.doSearch(filter); } onSubmit() { - this.filter = this.getObjectFromString(this.searchString); + this.doSearch(this.fromBar()); + } + + removeParam(index) { + this.params.splice(index, 1); + this.doSearch(this.fromBar()); + } + + doSearch(filter) { + this.filter = filter; + + let opts = this.$state.is(this.searchState) + ? {location: 'replace'} : null; + this.$state.go(this.searchState, + {q: JSON.stringify(filter)}, opts); + } + + fromBar() { + let filter = {}; + + if (this.searchString) + filter.search = this.searchString; + + if (this.params) { + for (let param of this.params) + filter[param.key] = param.value; + } + + return filter; + } + + toBar(filter) { + this.params = []; + this.searchString = filter && filter.search; + if (!filter) return; + + let keys = Object.keys(filter); + keys.forEach(key => { + if (key == 'search') return; + let value = filter[key]; + let chip; + + if (typeof value == 'string' + && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) + value = new Date(value); + + switch (typeof value) { + case 'boolean': + chip = `${value ? '' : 'not '}${key}`; + break; + case 'number': + case 'string': + chip = `${key}: ${value}`; + break; + default: + if (value instanceof Date) { + let format = 'yyyy-MM-dd'; + if (value.getHours() || value.getMinutes()) + format += ' HH:mm'; + chip = `${key}: ${this.$filter('date')(value, format)}`; + } else + chip = key; + } + + this.params.push({chip, key, value}); + }); + } +} + +ngModule.vnComponent('vnSearchbar', { + controller: Controller, + template: require('./searchbar.html'), + bindings: { + searchState: '@?', + filter: ' this.onStateChange()); + + this.fetchFilter(); + } + + $postLink() { + if (this.filter !== null) + this.doSearch(); + } + + $onDestroy() { + this.deregisterCallback(); + } + + fetchFilter() { + if (this.$state.params.q) { + try { + this.filter = JSON.parse(this.$state.params.q); + } catch (e) { + console.error(e); + } + } else + this.filter = null; + } + + onStateChange() { + this.fetchFilter(); + this.doSearch(); } doSearch() { - this.searchString = this.getStringFromObject(this.shownFilter); - - let filter = this._filter; + let filter = this.filter; if (filter == null && this.autoload) filter = {}; @@ -141,94 +267,17 @@ export default class Controller extends Component { exprBuilder(param, value) { return {[param]: value}; } - - /** - * Finds pattern key:value or key:(extra value) and passes it to object. - * - * @param {String} searchString The search string - * @return {Object} The parsed object - */ - getObjectFromString(searchString) { - let result = {}; - if (searchString) { - let regex = /((([\w_]+):([\w_]+))|([\w_]+):\(([\w_ ]+)\))/gi; - let findPattern = searchString.match(regex); - let remnantString = searchString.replace(regex, '').trim(); - if (findPattern) { - for (let i = 0; i < findPattern.length; i++) { - let aux = findPattern[i].split(':'); - let property = aux[0]; - let value = aux[1].replace(/\(|\)/g, ''); - result[property] = value.trim(); - } - } - if (remnantString) - result.search = remnantString; - } - return result; - } - - /** - * Passes an object to pattern key:value or key:(extra value). - * - * @param {Object} searchObject The search object - * @return {String} The passed string - */ - getStringFromObject(searchObject) { - let search = []; - - if (searchObject) { - let keys = Object.keys(searchObject); - keys.forEach(key => { - if (key == 'search') return; - - let value = searchObject[key]; - let valueString; - - if (typeof value === 'string' && value.indexOf(' ') !== -1) - valueString = `(${value})`; - else if (value instanceof Date) - valueString = value.toJSON(); - else { - switch (typeof value) { - case 'number': - case 'string': - case 'boolean': - valueString = `${value}`; - } - } - - if (valueString) - search.push(`${key}:${valueString}`); - }); - - if (searchObject.search) - search.unshift(searchObject.search); - } - - return search.length ? search.join(' ') : ''; - } - - $onDestroy() { - this.deregisterCallback(); - } } -Controller.$inject = ['$element', '$scope', '$compile', '$state', '$transitions']; +AutoSearch.$inject = ['$state', '$transitions']; -ngModule.component('vnSearchbar', { - template: require('./searchbar.html'), +ngModule.vnComponent('vnAutoSearch', { + controller: AutoSearch, bindings: { - filter: ' { let controller; let $element; let $state; + let $params; let $scope; + let filter = {id: 1, search: 'needle'}; beforeEach(ngModule('vnCore')); - beforeEach(angular.mock.inject(($componentController, _$state_, $rootScope) => { + beforeEach(angular.mock.inject(($componentController, $rootScope, _$state_) => { $scope = $rootScope.$new(); $state = _$state_; - $element = angular.element(``); + $params = $state.params; + + $params.q = JSON.stringify(filter); + + $element = angular.element(`
`); controller = $componentController('vnSearchbar', {$element, $scope}); controller.panel = 'vn-client-search-panel'; })); describe('$postLink()', () => { - it(`should not call onStateChange() if filter is defined`, () => { - spyOn(controller, 'onStateChange'); - controller.filter = {}; - + it(`should fetch the filter from the state if it's in the filter state`, () => { controller.$postLink(); - expect(controller.onStateChange).not.toHaveBeenCalledWith(); + expect(controller.filter).toEqual(filter); + expect(controller.searchString).toBe('needle'); + expect(controller.params.length).toBe(1); }); - it(`should call onStateChange() if filter is null`, () => { - spyOn(controller, 'onStateChange'); - controller.filter = null; - + it(`should not fetch the filter from the state if not in the filter state`, () => { + controller.searchState = 'other.state'; controller.$postLink(); - expect(controller.onStateChange).toHaveBeenCalledWith(); - }); - }); - - describe('onStateChange()', () => { - it(`should set a formated _filter in the controller`, () => { - spyOn(controller, 'doSearch'); - Object.assign($state.params, {q: '{"id": 999}'}); - - controller.onStateChange(); - - expect(controller._filter).toEqual({id: 999}); - expect(controller.doSearch).toHaveBeenCalledWith(); - }); - }); - - describe('shownFilter() getter', () => { - it(`should return the _filter if not NULL`, () => { - controller.filter = '{"id": 999}'; - - let shownFilter = controller.shownFilter; - - expect(shownFilter).toEqual(controller._filter); - expect(shownFilter).not.toEqual(controller.suggestedFilter); - }); - - it(`should return the suggested filter if filter is NULL`, () => { - controller.suggestedFilter = '{"id": 888}'; - - let shownFilter = controller.shownFilter; - - expect(shownFilter).not.toEqual(controller._filter); - expect(shownFilter).toEqual(controller.suggestedFilter); + expect(controller.filter).toBeNull(); + expect(controller.searchString).toBeNull(); + expect(controller.params.length).toBe(0); }); }); describe('filter() setter', () => { - it(`should call $state.go() to replace the current state location instead of creating a new one`, () => { - controller._filter = {}; - spyOn($state, 'go'); - controller.filter = {expected: 'filter'}; + it(`should update the bar params and search`, () => { + let withoutHours = new Date(2000, 1, 1); + let withHours = new Date(withoutHours.getTime()); + withHours.setHours(12, 30, 15, 10); - expect(controller._filter).toEqual(controller.filter); - expect($state.go).toHaveBeenCalledWith('.', Object({q: '{"expected":"filter"}'}), Object({location: 'replace'})); + controller.filter = { + search: 'needle', + withHours: withHours.toJSON(), + withoutHours: withoutHours.toJSON(), + boolean: true, + negated: false, + myObjectProp: {myProp: 1} + }; + + let chips = {}; + for (let param of controller.params || []) + chips[param.key] = param.chip; + + expect(controller.searchString).toBe('needle'); + expect(chips.withHours).toBe('withHours: 2000-02-01 12:30'); + expect(chips.withoutHours).toBe('withoutHours: 2000-02-01'); + expect(chips.boolean).toBe('boolean'); + expect(chips.negated).toBe('not negated'); + expect(chips.myObjectProp).toBe('myObjectProp'); + }); + }); + + describe('shownFilter() getter', () => { + it(`should return the _filter if not null`, () => { + controller.filter = filter; + controller.suggestedFilter = {sugestedParam: 'suggestedValue'}; + + expect(controller.shownFilter).toEqual(filter); + }); + + it(`should return the suggested filter if filter is null`, () => { + controller.filter = null; + controller.suggestedFilter = {sugestedParam: 'suggestedValue'}; + + expect(controller.shownFilter).toEqual(controller.suggestedFilter); + }); + }); + + describe('searchString() setter', () => { + it(`should clear the whole filter when it's null`, () => { + controller.filter = filter; + controller.searchString = null; + + expect(controller.searchString).toBeNull(); + expect(controller.params.length).toBe(0); }); }); describe('onPanelSubmit()', () => { - it(`should hide the popover, and set the filter as it wasn't defined`, () => { + it(`should compact and define the filter`, () => { controller.$.popover = {hide: jasmine.createSpy('hide')}; - const filter = undefined; + const filter = { + id: 1, + thisKeyShouldBePurged: null, + alsoThis: [], + andThis: {emptyProp: undefined, nullProp: null}, + dontForgetThis: [null, undefined], + myObject: {keepThis: true, butNotThis: null}, + myArray: [null, undefined, true] + }; controller.onPanelSubmit(filter); - expect(controller.filter).toMatchSnapshot(); - }); - - it(`should hide the popover, compact the filter`, () => { - controller.$.popover = {hide: jasmine.createSpy('hide')}; - const filter = {id: 999, thisKeyShouldBePurged: null}; - - controller.onPanelSubmit(filter); - - expect(controller.filter).toMatchSnapshot(); + expect(controller.filter).toEqual({ + id: 1, + myObject: {keepThis: true}, + myArray: [true] + }); }); }); describe('onSubmit()', () => { - it(`should define the controller's filter`, () => { - controller.searchString = 'id: 999'; + it(`should define the filter`, () => { + controller.filter = filter; + controller.searchString = 'mySearch'; controller.onSubmit(); - expect(controller.filter).toMatchSnapshot(); + expect(controller.filter).toEqual({id: 1, search: 'mySearch'}); + }); + }); + + describe('removeParam()', () => { + it(`should remove the parameter from the filter`, () => { + controller.filter = filter; + controller.removeParam(0); + + expect(controller.filter).toEqual({search: 'needle'}); }); }); describe('doSearch()', () => { - it(`should define searchString, call onSearch and the model applyFilter method`, () => { - controller._filter = 'id: 123456'; - controller.autoload = true; - controller.onSearch = jasmine.createSpy('onSearch'); - controller.model = {applyFilter: jasmine.createSpy('applyFilter')}; - controller.paramBuilder = jasmine.createSpy('paramBuilder').and.returnValue({'param': `${controller._filter}`}); + it(`should go to the search state and pass the filter as query param`, () => { + spyOn($state, 'go'); + controller.searchState = 'search.state'; + controller.doSearch(filter); + let queryParams = {q: JSON.stringify(filter)}; - controller.doSearch(); - - expect(controller.searchString).toMatchSnapshot(); - expect(controller.onSearch).toHaveBeenCalledWith({'$params': `${controller._filter}`}); - expect(controller.model.applyFilter).toHaveBeenCalledWith(jasmine.any(Object), jasmine.any(Object)); - }); - - it(`should define searchString, call onSearch and the model clear method`, () => { - controller._filter = null; - controller.autoload = false; - controller.onSearch = jasmine.createSpy('onSearch'); - controller.model = {clear: jasmine.createSpy('clear')}; - controller.paramBuilder = jasmine.createSpy('paramBuilder').and.returnValue({'param': `${controller._filter}`}); - - - controller.doSearch(); - - expect(controller.searchString).toEqual(''); - expect(controller.onSearch).toHaveBeenCalledWith({'$params': null}); - expect(controller.model.clear).toHaveBeenCalledWith(); - }); - }); - - describe('getObjectFromString()', () => { - it(`should return a formated object based on the string received for basic search`, () => { - let result = controller.getObjectFromString('Bruce Wayne'); - - expect(result).toEqual({search: 'Bruce Wayne'}); - }); - - it(`should return a formated object based on the string received for advanced search`, () => { - let result = controller.getObjectFromString('id:101 name:(Bruce Wayne)'); - - expect(result).toEqual({id: '101', name: 'Bruce Wayne'}); - }); - - it(`should format the object grouping any unmatched part of the instring of the string to the search property`, () => { - let string = 'I am the search id:101 name:(Bruce Wayne) concatenated value'; - let result = controller.getObjectFromString(string); - - expect(result).toEqual({ - id: '101', - name: 'Bruce Wayne', - search: 'I am the search concatenated value' - }); + expect($state.go).toHaveBeenCalledWith('search.state', queryParams, null); + expect(controller.filter).toEqual(filter); }); }); }); diff --git a/front/core/components/searchbar/style.scss b/front/core/components/searchbar/style.scss index 03cc0cd8d..31907792c 100644 --- a/front/core/components/searchbar/style.scss +++ b/front/core/components/searchbar/style.scss @@ -2,7 +2,40 @@ vn-searchbar { display: block; - width: 100%; + max-width: 35em; + margin: 0 auto; + + .search-params { + flex: 1; + margin: .05em 0; + overflow: visible; + display: flex; + max-width: 24em; + + & > .search-param { + color: rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, .1); + padding: .1em .4em; + margin-left: .2em; + display: inline-block; + border-radius: .8em; + max-width: 12em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + & > vn-icon { + font-size: inherit; + vertical-align: middle; + cursor: pointer; + border-radius: 50%; + + &:hover { + color: rgba(0, 0, 0, .8); + } + } + } + } } .search-panel { diff --git a/front/core/components/slot/index.js b/front/core/components/slot/index.js new file mode 100644 index 000000000..1747ed93f --- /dev/null +++ b/front/core/components/slot/index.js @@ -0,0 +1,81 @@ +import ngModule from '../../module'; +import './portal'; +import './style.scss'; + +export class Slot { + constructor($element, vnSlotService) { + this.$element = $element; + this.vnSlotService = vnSlotService; + this.$content = null; + } + + $onDestroy() { + this.unregister(); + } + + set name(value) { + this.unregister(); + this._name = value; + this.vnSlotService.slots[value] = this; + } + + get name() { + return this._name; + } + + unregister() { + if (this.name) + this.vnSlotService.slots[this.name] = undefined; + } + + setContent($content) { + if (this.$content) { + this.$content.detach(); + this.$content = null; + } + + this.$content = $content; + if (this.$content) this.$element.append($content); + this.$element[0].style.display = $content ? 'block' : 'none'; + } +} +Slot.$inject = ['$element', 'vnSlotService']; + +ngModule.vnComponent('vnSlot', { + controller: Slot, + bindings: { + name: '@?' + } +}); + +export class SlotService { + constructor() { + this.stacks = {}; + this.slots = {}; + } + + push(slot, $transclude) { + if (!this.stacks[slot]) this.stacks[slot] = []; + this.stacks[slot].unshift($transclude); + this.refreshContent(slot); + } + + pop(slot) { + let $content = this.stacks[slot].shift(); + this.refreshContent(slot); + if ($content && typeof $content == 'object') + $content.remove(); + } + + refreshContent(slot) { + if (!this.slots[slot]) return; + let $content = this.stacks[slot][0]; + if (typeof $content == 'function') { + $content(clone => { + $content = this.stacks[slot][0] = clone; + }); + } + this.slots[slot].setContent($content); + } +} +ngModule.service('vnSlotService', SlotService); diff --git a/front/core/components/slot/portal.js b/front/core/components/slot/portal.js new file mode 100644 index 000000000..f4dbecb09 --- /dev/null +++ b/front/core/components/slot/portal.js @@ -0,0 +1,28 @@ +import ngModule from '../../module'; + +/** + * Component used to fill slots with content. + */ +export default class Portal { + constructor($transclude, vnSlotService) { + this.$transclude = $transclude; + this.vnSlotService = vnSlotService; + } + + $postLink() { + this.vnSlotService.push(this.slot, this.$transclude); + } + + $onDestroy() { + this.vnSlotService.pop(this.slot); + } +} +Portal.$inject = ['$transclude', 'vnSlotService']; + +ngModule.component('vnPortal', { + controller: Portal, + transclude: true, + bindings: { + slot: '@' + } +}); diff --git a/front/core/components/slot/style.scss b/front/core/components/slot/style.scss new file mode 100644 index 000000000..825aad085 --- /dev/null +++ b/front/core/components/slot/style.scss @@ -0,0 +1,3 @@ +vn-slot { + display: block; +} \ No newline at end of file diff --git a/front/core/components/spinner/spinner.html b/front/core/components/spinner/spinner.html index b6a3af8be..440e47445 100644 --- a/front/core/components/spinner/spinner.html +++ b/front/core/components/spinner/spinner.html @@ -6,6 +6,7 @@ cy="50" r="20" fill="none" + stroke="currentColor" stroke-miterlimit="10"> diff --git a/front/core/components/spinner/style.scss b/front/core/components/spinner/style.scss index 3be908caf..eb39f64e8 100644 --- a/front/core/components/spinner/style.scss +++ b/front/core/components/spinner/style.scss @@ -4,6 +4,7 @@ vn-spinner { display: inline-block; min-height: 28px; min-width: 28px; + color: $color-main; & > .loader { position: relative; @@ -29,7 +30,6 @@ vn-spinner { margin: auto; & > .path { - stroke: $color-main; stroke-dasharray: 1, 200; stroke-dashoffset: 0; stroke-linecap: square; diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss index dc3c754bc..41d1f6db8 100644 --- a/front/core/components/table/style.scss +++ b/front/core/components/table/style.scss @@ -3,8 +3,8 @@ vn-table { display: block; - overflow: auto; width: 100%; + overflow: auto; } .vn-table { width: 100%; diff --git a/front/core/components/toggle/style.scss b/front/core/components/toggle/style.scss index af3dc3ae5..31769d2a5 100644 --- a/front/core/components/toggle/style.scss +++ b/front/core/components/toggle/style.scss @@ -40,10 +40,10 @@ background-color: rgba(0, 0, 0, .1); } &.checked > .btn { - border-color: $color-main; + border-color: $color-button; & > .focus-mark { - background-color: rgba($color-main, .15); + background-color: rgba($color-button, .15); } } &.disabled { diff --git a/front/core/components/treeview/style.scss b/front/core/components/treeview/style.scss index bd6175021..7cd8c8bea 100644 --- a/front/core/components/treeview/style.scss +++ b/front/core/components/treeview/style.scss @@ -36,13 +36,14 @@ vn-treeview-childs { } vn-treeview-child { + line-height: 38px; font-size: 16px; display: block; .node { @extend %clickable; display: flex; - padding: 5px; + padding: 2px; align-items: center; } & > div > .arrow { diff --git a/front/core/components/wday-picker/index.html b/front/core/components/wday-picker/index.html index e8d74f1c2..4262431e6 100644 --- a/front/core/components/wday-picker/index.html +++ b/front/core/components/wday-picker/index.html @@ -2,7 +2,7 @@ + ng-class="{marked: $ctrl.field[day.index]}" + ng-click="$ctrl.toggleDay(day.index)"> {{day.localeChar}} \ No newline at end of file diff --git a/front/core/components/wday-picker/index.js b/front/core/components/wday-picker/index.js index 3cbb288e0..94a6d424c 100644 --- a/front/core/components/wday-picker/index.js +++ b/front/core/components/wday-picker/index.js @@ -8,6 +8,13 @@ export default class WdayPicker extends FormInput { this.days = vnWeekDays.locales; this.initTabIndex(); } + + toggleDay(index) { + let field = this.field; + if (!field) field = []; + field[index] = !field[index]; + this.change(field); + } } WdayPicker.$inject = ['$element', '$scope', 'vnWeekDays']; diff --git a/front/core/components/wday-picker/style.scss b/front/core/components/wday-picker/style.scss index c6899ad6a..be610c733 100644 --- a/front/core/components/wday-picker/style.scss +++ b/front/core/components/wday-picker/style.scss @@ -20,7 +20,7 @@ background-color: rgba(0, 0, 0, .05); &.marked { - background: $color-main; + background: $color-button; color: $color-font-dark; } } diff --git a/front/core/directives/id.js b/front/core/directives/id.js index 693606b0c..8230527d6 100644 --- a/front/core/directives/id.js +++ b/front/core/directives/id.js @@ -12,14 +12,23 @@ export function directive() { restrict: 'A', link: function($scope, $element, $attrs) { let id = kebabToCamel($attrs.vnId); - let $ctrl = $element[0].$ctrl - ? $element[0].$ctrl - : $element.controller($element[0].tagName.toLowerCase()); if (!id) throw new Error(`vnId: Attribute can't be null`); - $scope[id] = $ctrl || $element[0]; + let $ctrl = $element[0].$ctrl + ? $element[0].$ctrl + : $element.controller($element[0].tagName.toLowerCase()); + let ctrl = $ctrl || $element[0]; + + $scope[id] = ctrl; + + if (!$scope.hasOwnProperty('$ctrl')) { + while ($scope && !$scope.hasOwnProperty('$ctrl')) + $scope = Object.getPrototypeOf($scope); + + if ($scope) $scope[id] = ctrl; + } } }; } diff --git a/front/core/lib/component.js b/front/core/lib/component.js index 979420b22..b9f04dba6 100644 --- a/front/core/lib/component.js +++ b/front/core/lib/component.js @@ -107,6 +107,7 @@ function runFn( $compile, $filter, $interpolate, + $window, vnApp) { Object.assign(Component.prototype, { $translate, @@ -119,6 +120,7 @@ function runFn( $compile, $filter, $interpolate, + $window, vnApp }); } @@ -133,6 +135,7 @@ runFn.$inject = [ '$compile', '$filter', '$interpolate', + '$window', 'vnApp' ]; diff --git a/front/core/lib/focus.js b/front/core/lib/focus.js new file mode 100644 index 000000000..8b997d920 --- /dev/null +++ b/front/core/lib/focus.js @@ -0,0 +1,16 @@ + +import isMobile from './is-mobile'; + +export default function focus(element) { + if (isMobile) return; + setTimeout(() => element.focus(), 10); +} + +export function select(element) { + if (isMobile) return; + setTimeout(() => { + element.focus(); + if (element.select) + element.select(); + }, 10); +} diff --git a/front/core/lib/index.js b/front/core/lib/index.js index 2682dfdf9..4d562ca05 100644 --- a/front/core/lib/index.js +++ b/front/core/lib/index.js @@ -2,8 +2,9 @@ import './module-loader'; import './crud'; import './copy'; import './equals'; +import './focus'; +import './get-main-route'; import './modified'; import './key-codes'; import './http-error'; import './user-error'; -import './get-main-route'; diff --git a/front/core/lib/specs/acl-service.spec.js b/front/core/lib/specs/acl-service.spec.js index 7756347b0..3a1460241 100644 --- a/front/core/lib/specs/acl-service.spec.js +++ b/front/core/lib/specs/acl-service.spec.js @@ -39,7 +39,7 @@ describe('Service acl', () => { expect(hasAny).toBeTruthy(); }); - it('should return true when user has not any of the passed roles', () => { + it('should return false when user has not any of the passed roles', () => { let hasAny = aclService.hasAny(['inventedRole', 'nonExistent']); expect(hasAny).toBeFalsy(); diff --git a/front/core/module.js b/front/core/module.js index 33eb68c24..0c52fd565 100644 --- a/front/core/module.js +++ b/front/core/module.js @@ -1,9 +1,6 @@ import {ng, ngDeps} from './vendor'; import {camelToKebab} from './lib/string'; -const ngModule = ng.module('vnCore', ngDeps); -export default ngModule; - /** * Acts like native Module.component() function but merging component options * with parent component options. This method establishes the $options property @@ -17,7 +14,7 @@ export default ngModule; * @param {Object} options The component options * @return {angularModule} The same angular module */ -ngModule.vnComponent = function(name, options) { +function vnComponent(name, options) { let controller = options.controller; let parent = Object.getPrototypeOf(controller); let parentOptions = parent.$options || {}; @@ -57,10 +54,21 @@ ngModule.vnComponent = function(name, options) { controller.$classNames = classNames; return this.component(name, mergedOptions); +} + +const ngModuleFn = ng.module; + +ng.module = function(...args) { + let ngModule = ngModuleFn.apply(this, args); + ngModule.vnComponent = vnComponent; + return ngModule; }; -config.$inject = ['$translateProvider', '$translatePartialLoaderProvider']; -export function config($translateProvider, $translatePartialLoaderProvider) { +const ngModule = ng.module('vnCore', ngDeps); +export default ngModule; + +config.$inject = ['$translateProvider', '$translatePartialLoaderProvider', '$animateProvider']; +export function config($translateProvider, $translatePartialLoaderProvider, $animateProvider) { // For CSS browser targeting document.documentElement.setAttribute('data-browser', navigator.userAgent); @@ -91,5 +99,8 @@ export function config($translateProvider, $translatePartialLoaderProvider) { return langAliases[locale]; return fallbackLang; }); + + $animateProvider.customFilter( + node => node.tagName == 'UI-VIEW'); } ngModule.config(config); diff --git a/front/core/services/interceptor.js b/front/core/services/interceptor.js index 448b70a34..b7cf3a0ec 100644 --- a/front/core/services/interceptor.js +++ b/front/core/services/interceptor.js @@ -13,11 +13,15 @@ function interceptor($q, vnApp, vnToken, $translate) { vnApp.pushLoader(); if (config.url.charAt(0) !== '/' && apiPath) - config.url = `${apiPath}/${config.url}`; + config.url = `${apiPath}${config.url}`; if (vnToken.token) config.headers.Authorization = vnToken.token; if ($translate.use()) config.headers['Accept-Language'] = $translate.use(); + if (config.filter) { + if (!config.params) config.params = {}; + config.params.filter = config.filter; + } return config; }, diff --git a/front/core/services/week-days.js b/front/core/services/week-days.js index f7d432c01..698d6df2c 100644 --- a/front/core/services/week-days.js +++ b/front/core/services/week-days.js @@ -1,5 +1,22 @@ import ngModule from '../module'; +/** + * @property {Array} days Weekdays data array with the same indexes as Date.getDay() + * @property {Object} map Weekdays data map using weekday codes as key + * @property {Array} localeCodes Locale weekday codes indexes depend on current locale + * @property {Array} locale Weekday data array with indexes depending on current locale + * + * Weekday properties: + * + * @property {Number} index The weekday index acording to Date.getDay() + * @property {String} code The weekday code + * @property {String} name The weekday name + * @property {String} char The first weekday letter + * @property {String} abr The abreviated 3 letters weekday name + * @property {String} locale The weekday name in current locale + * @property {String} localeChar The first weekday letter in current locale + * @property {String} localeAbr The abreviated 3 letters weekday name in current locale + */ class WeekDays { constructor($translate) { this.$translate = $translate; @@ -65,6 +82,34 @@ class WeekDays { for (let code of this.localeCodes) this.locales.push(this.map[code]); } + + fromSet(weekDays) { + let wdays = []; + + if (weekDays) { + let codes = weekDays.split(','); + for (let code of codes) { + let data = this.map[code]; + if (data) wdays[data.index] = true; + } + } + + return wdays; + } + + toSet(wdays) { + let weekDays = []; + + if (wdays) { + for (let i = 0; i < wdays.length; i++) { + if (!wdays[i]) continue; + let data = this.days[i]; + if (data) weekDays.push(data.code); + } + } + + return weekDays.join(','); + } } WeekDays.$inject = ['$translate']; diff --git a/front/core/styles/animations.scss b/front/core/styles/animations.scss new file mode 100644 index 000000000..039f79c77 --- /dev/null +++ b/front/core/styles/animations.scss @@ -0,0 +1,30 @@ + +@keyframes nothing {} +@keyframes slideIn { + from { + transform: translate3d(-2em, 0, 0); + opacity: 0; + } + to { + transform: translate3d(0, 0, 0); + opacity: 1; + } +} +@keyframes amplify { + from { + opacity: 0; + transform: scale3d(0, 0, 0); + } + to { + opacity: 1; + transform: scale3d(1, 1, 1); + } +} +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} \ No newline at end of file diff --git a/front/core/styles/global.scss b/front/core/styles/global.scss index ff49516dc..8b3d465f1 100644 --- a/front/core/styles/global.scss +++ b/front/core/styles/global.scss @@ -40,4 +40,8 @@ button { a { color: $color-font-link; text-decoration: none; +} +.ng-leave, +.ng-enter { + transition: none; } \ No newline at end of file diff --git a/front/core/styles/index.js b/front/core/styles/index.js index 1cdc4c7e1..dd35b7bfd 100644 --- a/front/core/styles/index.js +++ b/front/core/styles/index.js @@ -1,3 +1,4 @@ +import './animations.scss'; import './background.scss'; import './border.scss'; import './font-family.scss'; diff --git a/front/core/styles/variables.scss b/front/core/styles/variables.scss index 454bc6f7c..89e487ad7 100644 --- a/front/core/styles/variables.scss +++ b/front/core/styles/variables.scss @@ -19,27 +19,36 @@ $spacing-xs: 4px; $spacing-sm: 8px; $spacing-md: 16px; $spacing-lg: 32px; -$spacing-xl: 100px; +$spacing-xl: 70px; // Light theme -$color-header: #3d3d3d; -$color-bg: #e5e5e5; -$color-bg-dark: #3d3d3d; +$color-primary: #f7931e; +$color-secondary: $color-primary; + $color-font: #222; $color-font-light: #555; $color-font-secondary: #9b9b9b; $color-font-dark: white; -$color-font-bg: rgba(0, 0, 0, .7); $color-font-link: #005a9a; +$color-font-bg: rgba(0, 0, 0, .7); +$color-font-bg-marginal: rgba(0, 0, 0, .4); +$color-font-bg-dark: rgba(255, 255, 255, .7); +$color-font-bg-dark-marginal: rgba(255, 255, 255, .4); + +$color-header: #3d3d3d; +$color-menu-header: #3d3d3d; +$color-bg: #e5e5e5; +$color-bg-dark: #3d3d3d; $color-active: #3d3d3d; -$color-active-font: white; +$color-active-font: $color-font-dark; $color-bg-panel: white; -$color-main: #f7931e; -$color-secondary: #ccc; +$color-main: $color-primary; +$color-marginal: #ccc; $color-success: #a3d131; $color-notice: #32b1ce; $color-alert: #f42121; +$color-button: $color-secondary; $color-spacer: rgba(0, 0, 0, .3); $color-spacer-light: rgba(0, 0, 0, .12); @@ -78,7 +87,7 @@ $color-active: #666; $color-active-font: white; $color-bg-panel: #3c3b3b; $color-main: #f7931e; -$color-secondary: #ccc; +$color-marginal: #ccc; $color-success: #a3d131; $color-notice: #32b1ce; $color-alert: #f42121; diff --git a/front/core/vendor.js b/front/core/vendor.js index 0cdae4e81..8cec57f4f 100644 --- a/front/core/vendor.js +++ b/front/core/vendor.js @@ -2,6 +2,7 @@ import '@babel/polyfill'; import * as ng from 'angular'; export {ng}; +import 'angular-animate'; import 'angular-translate'; import 'angular-translate-loader-partial'; import '@uirouter/angularjs'; @@ -9,6 +10,7 @@ import 'mg-crud'; import 'oclazyload'; export const ngDeps = [ + 'ngAnimate', 'pascalprecht.translate', 'ui.router', 'mgCrud', diff --git a/front/package-lock.json b/front/package-lock.json index 9d71d20cc..f546f6c65 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -31,6 +31,11 @@ "resolved": "https://registry.npmjs.org/angular/-/angular-1.7.5.tgz", "integrity": "sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw==" }, + "angular-animate": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.8.tgz", + "integrity": "sha512-bINtzizq7TbJzfVrDpwLfTxVl0Qd7fRNWFb5jKYI1vaFZobQNX/QONXlLow6ySsDbZ6eLECycB7mvWtfh1YYaw==" + }, "angular-translate": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.1.tgz", diff --git a/front/package.json b/front/package.json index e17dbd08c..1eb3a8b8e 100644 --- a/front/package.json +++ b/front/package.json @@ -12,6 +12,7 @@ "@babel/polyfill": "^7.2.5", "@uirouter/angularjs": "^1.0.20", "angular": "^1.7.5", + "angular-animate": "^1.7.8", "angular-translate": "^2.18.1", "angular-translate-loader-partial": "^2.18.1", "js-yaml": "^3.13.1", diff --git a/front/salix/components/app/app.html b/front/salix/components/app/app.html index d6ac05d7e..5432609c2 100644 --- a/front/salix/components/app/app.html +++ b/front/salix/components/app/app.html @@ -1,29 +1,9 @@ - - - - -
- {{$ctrl.$state.current.description}} -
- - - - -
-
- -
+ + + + \ No newline at end of file diff --git a/front/salix/components/app/app.js b/front/salix/components/app/app.js index edf482712..1f8cdb46e 100644 --- a/front/salix/components/app/app.js +++ b/front/salix/components/app/app.js @@ -1,5 +1,6 @@ import ngModule from '../../module'; import './style.scss'; +import Component from 'core/lib/component'; /** * The main graphical application component. @@ -7,28 +8,21 @@ import './style.scss'; * @property {SideMenu} leftMenu The left menu, if it's present * @property {SideMenu} rightMenu The left menu, if it's present */ -export default class App { - constructor($, $state, vnApp) { - Object.assign(this, { - $, - $state, - vnApp - }); - } - +export default class App extends Component { $postLink() { this.vnApp.logger = this; } - $onDestroy() { - this.vnApp.logger = null; - } - - get showTopbar() { + get showLayout() { let state = this.$state.current.name; return state && state != 'login'; } + $onDestroy() { + this.deregisterCallback(); + this.vnApp.logger = null; + } + showMessage(message) { this.$.snackbar.show({message: message}); } @@ -41,9 +35,8 @@ export default class App { this.$.snackbar.showError({message: message}); } } -App.$inject = ['$scope', '$state', 'vnApp']; -ngModule.component('vnApp', { +ngModule.vnComponent('vnApp', { template: require('./app.html'), controller: App }); diff --git a/front/salix/components/app/style.scss b/front/salix/components/app/style.scss index 530524773..aab37c8a3 100644 --- a/front/salix/components/app/style.scss +++ b/front/salix/components/app/style.scss @@ -1,99 +1,17 @@ @import "variables"; vn-app { - height: inherit; display: block; + height: inherit; - & > vn-topbar { - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 10; - box-shadow: 0 .1em .2em $color-shadow; - height: $topbar-height; - padding: .4em; - - & > header { - & > * { - padding: .3em; - } - & > .logo > img { - height: 1.4em; - display: block; - } - & > .show-menu { - display: none; - font-size: 1.8em; - cursor: pointer; - - &:hover { - color: $color-main; - } - } - & > .main-title { - font-size: 1.6em; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - padding-left: .6em; - } - & > vn-spinner { - padding: 0 .4em; - } - & > vn-main-menu { - flex: 1; - } - } - } - & > .main-view { + ui-view { + display: block; box-sizing: border-box; height: inherit; - - &.padding { - padding-top: $topbar-height; - } - .content-block { - box-sizing: border-box; - padding: $spacing-md; - height: inherit; - } - vn-main-block { - display: block; - margin: 0 auto; - padding-left: $menu-width; - height: 100% - - } - .main-with-right-menu { - padding-right: $menu-width; - - @media screen and (max-width: 800px) { - padding-right: 0; - } - } - } - @media screen and (max-width: $mobile-width) { - & > vn-topbar > header { - & > .logo { - display: none; - } - & > .show-menu { - display: block; - } - } - & > .main-view { - .content-block { - margin-left: 0; - margin-right: 0; - } - vn-main-block { - padding-left: 0; - } - .main-with-right-menu { - padding-right: 0; - } + &.ng-enter { + animation-name: nothing, slideIn; + animation-duration: 400ms, 200ms; } } } diff --git a/front/salix/components/descriptor/style.scss b/front/salix/components/descriptor/style.scss index 16bc23b28..af719cc0d 100644 --- a/front/salix/components/descriptor/style.scss +++ b/front/salix/components/descriptor/style.scss @@ -50,7 +50,7 @@ & > vn-icon { padding: $spacing-sm; - color: $color-secondary; + color: $color-marginal; font-size: 1.5em; &.bright { diff --git a/front/salix/components/home/home.html b/front/salix/components/home/home.html index 47307d22e..b3fc02d0d 100644 --- a/front/salix/components/home/home.html +++ b/front/salix/components/home/home.html @@ -1,4 +1,4 @@ -
+
{ let $httpBackend; @@ -14,8 +14,7 @@ describe('Component vnMainMenu', () => { describe('getCurrentUserName()', () => { it(`should set the user name property in the controller`, () => { - $httpBackend.when('GET', `Accounts/getCurrentUserData`).respond({name: 'batman'}); - $httpBackend.expect('GET', `Accounts/getCurrentUserData`); + $httpBackend.expect('GET', `Accounts/getUserData`).respond({name: 'batman'}); controller.getCurrentUserName(); $httpBackend.flush(); diff --git a/front/salix/components/layout/index.html b/front/salix/components/layout/index.html new file mode 100644 index 000000000..0ad400114 --- /dev/null +++ b/front/salix/components/layout/index.html @@ -0,0 +1,77 @@ + + + +
+ +
+ {{$ctrl.$state.current.description}} +
+ + +
+ +
+ + + + + + +
+ +
    +
  • + + Home +
  • +
  • + + {{::mod.name}} +
  • +
+
+ + + + + + + + + + + + + + {{::mod.name}} + + + + + \ No newline at end of file diff --git a/front/salix/components/layout/index.js b/front/salix/components/layout/index.js new file mode 100644 index 000000000..883b010ba --- /dev/null +++ b/front/salix/components/layout/index.js @@ -0,0 +1,30 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export class Layout extends Component { + constructor($element, $, vnModules) { + super($element, $); + this.modules = vnModules.get(); + } + + $onInit() { + this.getUserData(); + } + + getUserData() { + this.$http.get('Accounts/getCurrentUserData').then(json => { + this.$.$root.user = json.data; + window.localStorage.currentUserWorkerId = json.data.workerId; + }); + } +} +Layout.$inject = ['$element', '$scope', 'vnModules']; + +ngModule.component('vnLayout', { + template: require('./index.html'), + controller: Layout, + require: { + app: '^vnApp' + } +}); diff --git a/front/salix/components/layout/index.spec.js b/front/salix/components/layout/index.spec.js new file mode 100644 index 000000000..7163acb65 --- /dev/null +++ b/front/salix/components/layout/index.spec.js @@ -0,0 +1,25 @@ +import './index.js'; + +describe('Component vnLayout', () => { + let $httpBackend; + let controller; + + beforeEach(ngModule('salix')); + + beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => { + let vnModules = {get: () => {}}; + $httpBackend = _$httpBackend_; + let $element = angular.element('
'); + controller = $componentController('vnLayout', {$element, vnModules}); + })); + + describe('getUserData()', () => { + it(`should set the user name property in the controller`, () => { + $httpBackend.expect('GET', `Accounts/getCurrentUserData`).respond({name: 'batman'}); + controller.getUserData(); + $httpBackend.flush(); + + expect(controller.$.$root.user.name).toEqual('batman'); + }); + }); +}); diff --git a/front/salix/components/app/logo.svg b/front/salix/components/layout/logo-text.svg similarity index 100% rename from front/salix/components/app/logo.svg rename to front/salix/components/layout/logo-text.svg diff --git a/front/salix/components/layout/logo.svg b/front/salix/components/layout/logo.svg new file mode 100644 index 000000000..1a111f2f1 --- /dev/null +++ b/front/salix/components/layout/logo.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/front/salix/components/layout/style.scss b/front/salix/components/layout/style.scss new file mode 100644 index 000000000..9a9d903a6 --- /dev/null +++ b/front/salix/components/layout/style.scss @@ -0,0 +1,159 @@ +@import "effects"; + +vn-layout { + & > vn-topbar { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 10; + box-shadow: 0 .1em .2em $color-shadow; + height: $topbar-height; + padding: 0 1em; + justify-content: space-between; + + & > .side { + flex: auto; + display: flex; + align-items: center; + width: 5em; + transition: width 200ms; + } + & > .start { + padding-right: 1em; + overflow: hidden; + + & > .logo > img { + height: 2em; + display: block; + } + & > .main-title { + font-size: 1.6em; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + padding-left: .4em; + } + & > vn-spinner { + padding: 0 .4em; + } + } + & > vn-slot { + flex: auto; + } + & > .end { + padding-left: 1em; + justify-content: flex-end; + } + .show-menu { + display: none; + } + .vn-button { + color: inherit; + font-size: 1.05em; + padding: 0; + } + } + & > vn-side-menu > .menu { + display: flex; + flex-direction: column; + align-items: stretch; + + & > vn-slot { + flex: 1; + overflow: auto; + } + } + &.left-menu { + & > vn-topbar > .start { + width: 5em + $menu-width; + } + & > .main-view { + padding-left: $menu-width; + } + } + &.right-menu { + & > vn-topbar > .end { + width: 5em + $menu-width; + } + & > .main-view { + padding-right: $menu-width; + } + } + & > .main-view { + padding-top: $topbar-height; + } + ui-view { + & > * { + display: block; + padding: $spacing-md; + } + &.ng-enter { + vn-side-menu { + opacity: 0; + animation: fadeIn 200ms ease-out 200ms; + } + [fixed-bottom-right] { + opacity: 0; + animation: amplify 200ms ease-out 200ms; + } + } + } + @media screen and (max-width: $mobile-width) { + & > vn-topbar { + & > .start > .logo { + display: none; + } + & > .side { + flex: 1; + } + .show-menu { + display: block; + } + } + &.left-menu { + & > vn-topbar { + left: 0; + } + & > .main-view { + padding-left: 0; + } + } + &.right-menu { + & > .main-view { + padding-right: 0; + } + } + ui-view > * { + padding-left: 0; + padding-right: 0; + } + } +} +.vn-popover .modules-menu { + list-style-type: none; + margin: 0; + color: $color-font-dark; + + & > li { + @extend %clickable-light; + background-color: $color-main; + margin-bottom: .6em; + padding: .8em; + border-radius: .1em; + min-width: 8em; + white-space: nowrap; + + &:last-child { + margin-bottom: 0; + } + & > vn-icon { + padding-right: .3em; + vertical-align: middle; + } + } +} +#user { + font-size: 1.5em; + height: auto; +} diff --git a/front/salix/components/left-menu/left-menu.html b/front/salix/components/left-menu/left-menu.html index 12cea49a3..73488a24b 100644 --- a/front/salix/components/left-menu/left-menu.html +++ b/front/salix/components/left-menu/left-menu.html @@ -1,17 +1,30 @@ -
    +
    • - - - {{::item.description}} + + + + + {{::item.description}} + + + + -
        + diff --git a/front/salix/components/left-menu/left-menu.js b/front/salix/components/left-menu/left-menu.js index 576dcc2df..92adb02aa 100644 --- a/front/salix/components/left-menu/left-menu.js +++ b/front/salix/components/left-menu/left-menu.js @@ -6,24 +6,42 @@ export default class LeftMenu { this.$element = $element; this.$timeout = $timeout; this.$state = $state; + this.aclService = aclService; this.deregisterCallback = $transitions.onSuccess({}, () => this.activateItem()); + this.source = 'main'; this._depth = 3; + } + $onInit() { + this.items = this.fetchItems(); + this.activateItem(); + } + + set depth(value) { + this._depth = value; + this.activateItem(); + } + + get depth() { + return this._depth; + } + + fetchItems() { let states = this.$state.router.stateRegistry.states; let moduleIndex = this.$state.current.data.moduleIndex; let moduleFile = window.routes[moduleIndex] || []; - let menu = moduleFile.menu || []; + let menu = moduleFile.menus && moduleFile.menus[this.source] || []; let items = []; - function addItem(items, item) { + let addItem = (items, item) => { let state = states[item.state]; if (!state) return; state = state.self; let acl = state.data.acl; - if (acl && !aclService.hasAny(acl)) + if (acl && !this.aclService.hasAny(acl)) return; items.push({ @@ -31,7 +49,7 @@ export default class LeftMenu { description: state.description, state: item.state }); - } + }; for (let item of menu) { if (item.state) @@ -52,17 +70,7 @@ export default class LeftMenu { } } - this.items = items; - this.activateItem(); - } - - set depth(value) { - this._depth = value; - this.activateItem(); - } - - get depth() { - return this._depth; + return items; } activateItem() { @@ -93,9 +101,8 @@ export default class LeftMenu { item.active = !item.active; this.$timeout(() => { - let element = this.$element[0].querySelector('a[class="expanded"]'); - if (element) - element.scrollIntoView(); + let element = this.$element[0].querySelector('a.expanded'); + if (element) element.scrollIntoView(); }); } @@ -109,6 +116,7 @@ ngModule.component('vnLeftMenu', { template: require('./left-menu.html'), controller: LeftMenu, bindings: { + source: '@?', depth: ' .vn-list { + padding: $spacing-md 0; - & > li { - list-style: none; - display: block; - - & > ul > li > a { - padding-left: 2em + & > li > .vn-item { + & > [side] > vn-icon[icon="keyboard_arrow_down"] { + transition: transform 200ms; } - } - - & > li > a { - @extend %clickable; - padding: .5em 1em; - display: block; - color: inherit; - - & > vn-icon:nth-child(1) { - vertical-align: middle; - margin-right: .4em - } - - & > vn-icon:nth-child(2) { - float: right; - margin-left: .4em - } - } - - & > li > a.active { - @extend %active; - } - - & > li > a.expanded { - & > vn-icon[icon="keyboard_arrow_down"] { - transition: all 0.2s; + &.expanded > [side] > vn-icon[icon="keyboard_arrow_down"] { transform: rotate(180deg); } } - - & > li > a.collapsed { - & > vn-icon[icon="keyboard_arrow_down"] { - transition: all 0.2s; - transform: rotate(0deg); - } - } } } diff --git a/front/salix/components/login/login.js b/front/salix/components/login/login.js index 401d4a4d9..efb5e02bb 100644 --- a/front/salix/components/login/login.js +++ b/front/salix/components/login/login.js @@ -14,6 +14,7 @@ export default class Controller { remember: true }); } + submit() { this.loading = true; this.vnAuth.login(this.user, this.password, this.remember) @@ -28,6 +29,7 @@ export default class Controller { throw err; }); } + focusUser() { this.$.userField.select(); this.$.userField.focus(); diff --git a/front/salix/components/main-menu/main-menu.html b/front/salix/components/main-menu/main-menu.html deleted file mode 100644 index c583ffe0c..000000000 --- a/front/salix/components/main-menu/main-menu.html +++ /dev/null @@ -1,31 +0,0 @@ -
        -
        - {{$root.user.nickname}} -
        - - - - -
        - -
          -
        • - - {{::mod.name}} -
        • -
        -
        - - \ No newline at end of file diff --git a/front/salix/components/main-menu/main-menu.js b/front/salix/components/main-menu/main-menu.js deleted file mode 100644 index 93ff955e7..000000000 --- a/front/salix/components/main-menu/main-menu.js +++ /dev/null @@ -1,30 +0,0 @@ -import ngModule from '../../module'; -import './style.scss'; - -export default class MainMenu { - constructor($, $http, vnAuth, vnModules) { - Object.assign(this, { - $, - $http, - vnAuth, - modules: vnModules.get() - }); - } - - $onInit() { - this.getCurrentUserName(); - } - - getCurrentUserName() { - this.$http.get('Accounts/getCurrentUserData').then(json => { - this.$.$root.user = json.data; - window.localStorage.currentUserWorkerId = json.data.workerId; - }); - } -} -MainMenu.$inject = ['$scope', '$http', 'vnAuth', 'vnModules']; - -ngModule.component('vnMainMenu', { - template: require('./main-menu.html'), - controller: MainMenu -}); diff --git a/front/salix/components/main-menu/style.scss b/front/salix/components/main-menu/style.scss deleted file mode 100644 index cd38472ab..000000000 --- a/front/salix/components/main-menu/style.scss +++ /dev/null @@ -1,61 +0,0 @@ -@import "effects"; -@import "variables"; - -vn-main-menu { - display: flex; - align-items: center; - justify-content: flex-end; - - & > div { - display: flex; - align-items: center; - box-sizing: border-box; - - & > * { - transition: color 250ms ease-out; - } - & > #user { - vertical-align: middle; - font-weight: bold; - margin-right: .2em; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - cursor: pointer; - } - & > .vn-button { - font-size: 1.2em; - color: inherit; - padding: 0; - margin-left: .3em; - } - & > :hover { - color: $color-main; - opacity: 1; - } - } -} - -.vn-popover .modules-menu { - list-style-type: none; - margin: 0; - color: $color-font-dark; - - & > li { - @extend %clickable-light; - background-color: $color-main; - margin-bottom: .6em; - padding: .8em; - border-radius: .1em; - min-width: 8em; - white-space: nowrap; - - &:last-child { - margin-bottom: 0; - } - & > vn-icon { - padding-right: .3em; - vertical-align: middle; - } - } -} diff --git a/front/salix/components/module-card/index.js b/front/salix/components/module-card/index.js new file mode 100644 index 000000000..9131cc772 --- /dev/null +++ b/front/salix/components/module-card/index.js @@ -0,0 +1,29 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +/** + * Base class for module cards. + */ +export default class ModuleCard extends Component { + constructor($element, $) { + super($element, $); + this.element.classList.add('vn-module-card'); + } + + $onInit() { + this.reload(); + } + + /** + * Reloads the card data. Should be implemented or overriden by child + * classes. + */ + reload() { + throw new Error('ModuleCard::reload() method not implemented'); + } +} + +ngModule.vnComponent('vnModuleCard', { + controller: ModuleCard +}); diff --git a/front/salix/components/module-card/style.scss b/front/salix/components/module-card/style.scss new file mode 100644 index 000000000..0ae943efc --- /dev/null +++ b/front/salix/components/module-card/style.scss @@ -0,0 +1,5 @@ +@import "variables"; + +.vn-module-card { + padding: 0; +} \ No newline at end of file diff --git a/front/salix/components/module-main/index.html b/front/salix/components/module-main/index.html new file mode 100644 index 000000000..6e04f06d0 --- /dev/null +++ b/front/salix/components/module-main/index.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/front/salix/components/module-main/index.js b/front/salix/components/module-main/index.js new file mode 100644 index 000000000..39fbe42b8 --- /dev/null +++ b/front/salix/components/module-main/index.js @@ -0,0 +1,15 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export default class ModuleMain extends Component { + constructor($element, $) { + super($element, $); + this.element.classList.add('vn-module-main'); + } +} + +ngModule.vnComponent('vnModuleMain', { + template: require('./index.html'), + controller: ModuleMain +}); diff --git a/front/salix/components/module-main/style.scss b/front/salix/components/module-main/style.scss new file mode 100644 index 000000000..d9bdbd940 --- /dev/null +++ b/front/salix/components/module-main/style.scss @@ -0,0 +1,5 @@ +@import "variables"; + +.vn-module-main { + padding: 0; +} \ No newline at end of file diff --git a/front/salix/components/section/index.js b/front/salix/components/section/index.js new file mode 100644 index 000000000..a0c7484b4 --- /dev/null +++ b/front/salix/components/section/index.js @@ -0,0 +1,14 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +export default class Section extends Component { + constructor($element, $) { + super($element, $); + this.element.classList.add('vn-section'); + } +} + +ngModule.vnComponent('vnSection', { + controller: Section +}); diff --git a/front/salix/components/section/style.scss b/front/salix/components/section/style.scss new file mode 100644 index 000000000..e69de29bb diff --git a/front/salix/components/side-menu/side-menu.js b/front/salix/components/side-menu/side-menu.js index dfac732a1..c2b5b421e 100644 --- a/front/salix/components/side-menu/side-menu.js +++ b/front/salix/components/side-menu/side-menu.js @@ -1,4 +1,5 @@ import ngModule from '../../module'; +import Component from 'core/lib/component'; import './style.scss'; /** @@ -7,15 +8,10 @@ import './style.scss'; * @property {String} side [left|right] The side where the menu is displayed * @property {Boolean} shown Whether the menu it's currently displayed (Only for small viewports) */ -export default class SideMenu { - constructor($, $element, $window, $transitions) { - Object.assign(this, { - $, - $element, - $window, - $transitions, - side: 'left' - }); +export default class SideMenu extends Component { + constructor($element, $) { + super($element, $); + this.side = 'left'; } $onInit() { @@ -23,18 +19,25 @@ export default class SideMenu { if (this.side == 'right') { this.menu.classList.add('right'); - this.app.rightMenu = this; + this.layout.rightMenu = this; + this.layout.element.classList.add('right-menu'); } else { this.menu.classList.add('left'); - this.app.leftMenu = this; + this.layout.leftMenu = this; + this.layout.element.classList.add('left-menu'); } } $onDestroy() { - if (this.side == 'right') - this.app.rightMenu = null; - else - this.app.leftMenu = null; + if (this.side == 'right') { + this.layout.rightMenu = null; + this.layout.element.classList.remove('right-menu'); + } else { + this.layout.leftMenu = null; + this.layout.element.classList.remove('left-menu'); + } + + this.hide(); } onEscape(event) { @@ -50,6 +53,7 @@ export default class SideMenu { } show() { + if (this.shown) return; this.shown = true; this.handler = e => this.onEscape(e); this.$window.addEventListener('keydown', this.handler); @@ -57,12 +61,12 @@ export default class SideMenu { } hide() { + if (!this.shown) return; this.$window.removeEventListener('keydown', this.handler); this.stateHandler(); this.shown = false; } } -SideMenu.$inject = ['$scope', '$element', '$window', '$transitions']; ngModule.component('vnSideMenu', { template: require('./side-menu.html'), @@ -72,6 +76,6 @@ ngModule.component('vnSideMenu', { side: '@?' }, require: { - app: '^vnApp' + layout: '^vnLayout' } }); diff --git a/front/salix/components/side-menu/style.scss b/front/salix/components/side-menu/style.scss index 0054abfa7..239d15cf6 100644 --- a/front/salix/components/side-menu/style.scss +++ b/front/salix/components/side-menu/style.scss @@ -1,5 +1,8 @@ @import "variables"; +vn-side-menu { + display: block; +} vn-side-menu > .menu { display: block; position: fixed; @@ -20,14 +23,15 @@ vn-side-menu > .menu { } @media screen and (max-width: $mobile-width) { - top: 0; transition: transform 200ms ease-out; z-index: 15; + top: 0; &.left { transform: translateZ(0) translateX(-$menu-width); } &.right { + top: 0; transform: translateZ(0) translateX($menu-width); } &.shown { diff --git a/front/salix/components/summary/style.scss b/front/salix/components/summary/style.scss index 58a613088..f0d6ae038 100644 --- a/front/salix/components/summary/style.scss +++ b/front/salix/components/summary/style.scss @@ -59,4 +59,5 @@ .vn-popup .summary { border: none; box-shadow: none; + margin: 0; } diff --git a/front/salix/components/topbar/style.scss b/front/salix/components/topbar/style.scss index dc5273dbf..d7a5173f6 100644 --- a/front/salix/components/topbar/style.scss +++ b/front/salix/components/topbar/style.scss @@ -2,15 +2,8 @@ vn-topbar { display: flex; + align-items: center; color: $color-font-dark; box-sizing: border-box; background-color: $color-header; - align-items: center; - - & > header { - height: inherit; - width: inherit; - display: flex; - align-items: center; - } } diff --git a/front/salix/components/topbar/topbar.js b/front/salix/components/topbar/topbar.js index 583354ad1..423b033ce 100644 --- a/front/salix/components/topbar/topbar.js +++ b/front/salix/components/topbar/topbar.js @@ -1,7 +1 @@ -import ngModule from '../../module'; import './style.scss'; - -ngModule.component('vnTopbar', { - template: require('./topbar.html'), - transclude: true -}); diff --git a/front/salix/components/user-popover/index.html b/front/salix/components/user-popover/index.html index 1e17313f1..f34219d26 100644 --- a/front/salix/components/user-popover/index.html +++ b/front/salix/components/user-popover/index.html @@ -13,18 +13,33 @@
        - -
        -
        -
        - {{$root.user.nickname}} -
        -
        - {{::$root.user.name}} + +
        +
        +
        +
        + {{$root.user.nickname}} +
        +
        + {{::$root.user.name}} +
        + +
        - - + + My account
        diff --git a/front/salix/components/user-popover/index.js b/front/salix/components/user-popover/index.js index 5addec4a2..764b0cae9 100644 --- a/front/salix/components/user-popover/index.js +++ b/front/salix/components/user-popover/index.js @@ -12,11 +12,12 @@ let languages = { }; class Controller { - constructor($, $translate, vnConfig) { + constructor($, $translate, vnConfig, vnAuth) { Object.assign(this, { $, $translate, vnConfig, + vnAuth, lang: $translate.use(), langs: [] }); @@ -86,7 +87,7 @@ class Controller { this.$.popover.show(event.target); } } -Controller.$inject = ['$scope', '$translate', 'vnConfig']; +Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth']; ngModule.component('vnUserPopover', { template: require('./index.html'), diff --git a/front/salix/components/user-popover/style.scss b/front/salix/components/user-popover/style.scss index 4277b98f6..c8325c9f6 100644 --- a/front/salix/components/user-popover/style.scss +++ b/front/salix/components/user-popover/style.scss @@ -8,11 +8,8 @@ align-items: center; & > vn-icon { - font-size: 60px; - border-radius: 50%; - color: $color-font-dark; - background: $color-secondary; - padding: .1em; + font-size: 80px; + color: $color-font-bg-marginal; } & > div { display: flex; @@ -20,10 +17,21 @@ justify-content: space-between; flex: 1; - & > .user { - max-width: 10em; + & > div { + display: flex; padding-bottom: .5em; + + & > .user { + flex: 1; + max-width: 8em; + } } } } +} +#logout { + float: right; + height: initial; + vertical-align: middle; + font-size: 1em; } \ No newline at end of file diff --git a/front/salix/locale/es.yml b/front/salix/locale/es.yml index a7f1dc93a..2f65ab255 100644 --- a/front/salix/locale/es.yml +++ b/front/salix/locale/es.yml @@ -1,4 +1,5 @@ Applications: Aplicaciones +Account: Cuenta Language: Idioma Change language: Cambiar idioma Data saved!: ¡Datos guardados! diff --git a/front/salix/module.js b/front/salix/module.js index 58bdec105..2354e0dff 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -62,9 +62,10 @@ export function config($translatePartialLoaderProvider, $httpProvider, $compileP $translatePartialLoaderProvider.addPart(appName); $httpProvider.interceptors.push('vnInterceptor'); - // $compileProvider.debugInfoEnabled(false); - $compileProvider.commentDirectivesEnabled(false); - $compileProvider.cssClassDirectivesEnabled(false); + $compileProvider + .debugInfoEnabled(false) + .commentDirectivesEnabled(false) + .cssClassDirectivesEnabled(false); } ngModule.config(config); diff --git a/front/salix/routes.js b/front/salix/routes.js index 48a92795e..600907ff1 100644 --- a/front/salix/routes.js +++ b/front/salix/routes.js @@ -1,41 +1,23 @@ import ngModule from './module'; import getMainRoute from 'core/lib/get-main-route'; -function loader(moduleName) { - load.$inject = ['vnModuleLoader']; - function load(moduleLoader) { - return moduleLoader.load(moduleName); - } - return load; -} - config.$inject = ['$stateProvider', '$urlRouterProvider']; function config($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise('/'); - $stateProvider.state('home', { - url: '/', - template: '', - description: 'Home' - }); - $stateProvider.state('login', { - url: '/login?continue', - template: '', - description: 'Login' - }); - - function getParams(route) { - let params = ''; - let temporalParams = []; - - if (!route.params) - return params; - - Object.keys(route.params).forEach(key => { - temporalParams.push(`${key} = "${route.params[key]}"`); + $stateProvider + .state('login', { + url: '/login?continue', + description: 'Login', + views: { + login: {template: ''} + } + }) + .state('home', { + url: '/', + description: 'Home', + template: '' }); - return temporalParams.join(' '); - } for (let file in window.routes) { let routeFile = window.routes[file]; @@ -68,5 +50,26 @@ function config($stateProvider, $urlRouterProvider) { $stateProvider.state(route.state, configRoute); } } + + function getParams(route) { + let params = ''; + let temporalParams = []; + + if (!route.params) + return params; + + Object.keys(route.params).forEach(key => { + temporalParams.push(`${key} = "${route.params[key]}"`); + }); + return temporalParams.join(' '); + } + + function loader(moduleName) { + load.$inject = ['vnModuleLoader']; + function load(moduleLoader) { + return moduleLoader.load(moduleName); + } + return load; + } } ngModule.config(config); diff --git a/front/salix/styles/misc.scss b/front/salix/styles/misc.scss index 0ae30becd..1f2d79981 100644 --- a/front/salix/styles/misc.scss +++ b/front/salix/styles/misc.scss @@ -123,25 +123,14 @@ html [scrollable] { font-size: 0.7em } } -[compact], .compact { - margin-left: auto; - margin-right: auto; - max-width: $width-md; -} -vn-empty-rows { - display: block; - text-align: center; - padding: 1.5em; - box-sizing: border-box; -} /* XXX: Deprecated, use classes with text prefix */ [color-main] { color: $color-main; } -[color-secondary] { - color: $color-secondary; +[color-marginal] { + color: $color-marginal; } [uppercase], .uppercase { text-transform: uppercase; diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 175b90ef0..70293a965 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -111,9 +111,11 @@ "This phone already exists": "Este teléfono ya existe", "You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos", "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado", - "You cannot delete this ticket because is already invoiced, deleted or prepared": "No puedes eliminar este tiquet porque ya está facturado, eliminado o preparado", "You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada", "You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero", "Has deleted the ticket id": "Ha eliminado el ticket id [#{{id}}]({{{url}}})", - "You cannot remove this ticket because is already invoiced, deleted or prepared": "You cannot remove this ticket because is already invoiced, deleted or prepared" + "You should specify a date": "Debes especificar una fecha", + "You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fín", + "Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fín", + "You should mark at least one week day": "Debes marcar al menos un día de la semana" } \ No newline at end of file diff --git a/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js b/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js index 767ecff2e..3a806ce44 100644 --- a/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js +++ b/modules/agency/back/methods/agency/getAgenciesWithWarehouse.js @@ -1,14 +1,23 @@ module.exports = Self => { Self.remoteMethod('getAgenciesWithWarehouse', { description: 'Returns a list of agencies that can land a shipment on a day for an address and a warehouse', - accepts: [{ - arg: 'filter', - type: 'object', - required: true, - description: 'addressFk' - }], + accepts: [ + { + arg: 'addressFk', + type: 'number', + required: true + }, { + arg: 'landed', + type: 'date', + required: true + }, { + arg: 'warehouseFk', + type: 'number', + required: true + } + ], returns: { - type: 'object', + type: ['object'], root: true }, http: { @@ -17,10 +26,9 @@ module.exports = Self => { } }); - Self.getAgenciesWithWarehouse = async filter => { + Self.getAgenciesWithWarehouse = async(addressFk, landed, warehouseFk) => { let query = `CALL vn.zone_getWarehouse(?, ?, ?)`; - let result = await Self.rawSql(query, [filter.addressFk, filter.landed, filter.warehouseFk]); - + let [result] = await Self.rawSql(query, [addressFk, landed, warehouseFk]); return result; }; }; diff --git a/modules/agency/back/methods/agency/getLanded.js b/modules/agency/back/methods/agency/getLanded.js index cc2d0d8a1..6c5a5d911 100644 --- a/modules/agency/back/methods/agency/getLanded.js +++ b/modules/agency/back/methods/agency/getLanded.js @@ -47,6 +47,7 @@ module.exports = Self => { let rsIndex = stmts.push( `SELECT * FROM tmp.zoneGetLanded`) - 1; + stmts.push(`DROP TEMPORARY TABLE tmp.zoneGetLanded`); let sql = ParameterizedSQL.join(stmts, ';'); let landed = await Self.rawStmt(sql); diff --git a/modules/agency/back/methods/agency/landsThatDay.js b/modules/agency/back/methods/agency/landsThatDay.js index f92d2e285..87da63767 100644 --- a/modules/agency/back/methods/agency/landsThatDay.js +++ b/modules/agency/back/methods/agency/landsThatDay.js @@ -1,12 +1,17 @@ module.exports = Self => { Self.remoteMethod('landsThatDay', { description: 'Returns a list of agencies that can land a shipment on a day for an address', - accepts: [{ - arg: 'filter', - type: 'object', - required: true, - description: 'addressFk' - }], + accepts: [ + { + arg: 'addressFk', + type: 'number', + required: true + }, { + arg: 'landed', + type: 'date', + required: true + } + ], returns: { type: 'object', root: true @@ -17,14 +22,13 @@ module.exports = Self => { } }); - Self.landsThatDay = async filter => { + Self.landsThatDay = async(addressFk, landed) => { let query = ` CALL vn.zone_getAgency(?, ?); SELECT * FROM tmp.zoneGetAgency; DROP TEMPORARY TABLE tmp.zoneGetAgency; `; - - let result = await Self.rawSql(query, [filter.addressFk, filter.landed]); + let result = await Self.rawSql(query, [addressFk, landed]); return result[1]; }; diff --git a/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js b/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js index 3666ef7f2..614c33519 100644 --- a/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js +++ b/modules/agency/back/methods/agency/specs/getAgenciesWithWarehouse.spec.js @@ -3,14 +3,7 @@ const app = require('vn-loopback/server/server'); describe('Agency getAgenciesWithWarehouse()', () => { const today = new Date(); it('should return the agencies that can handle the given delivery request', async() => { - let filter = { - addressFk: 101, - landed: today, - warehouseFk: 1 - }; - - let result = await app.models.Agency.getAgenciesWithWarehouse(filter); - let agencies = result[0]; + let agencies = await app.models.Agency.getAgenciesWithWarehouse(101, today, 1); expect(agencies.length).toEqual(3); expect(agencies[0].agencyMode).toEqual('inhouse pickup'); @@ -19,14 +12,7 @@ describe('Agency getAgenciesWithWarehouse()', () => { }); it('should return no agencies if the date is incorrect', async() => { - let filter = { - addressFk: 101, - landed: null, - warehouseFk: 1 - }; - - let result = await app.models.Agency.getAgenciesWithWarehouse(filter); - let agencies = result[0]; + let agencies = await app.models.Agency.getAgenciesWithWarehouse(101, null, 1); expect(agencies.length).toEqual(0); }); diff --git a/modules/agency/back/methods/agency/specs/getLanded.spec.js b/modules/agency/back/methods/agency/specs/getLanded.spec.js index 7582751cb..15c0f1ca9 100644 --- a/modules/agency/back/methods/agency/specs/getLanded.spec.js +++ b/modules/agency/back/methods/agency/specs/getLanded.spec.js @@ -1,8 +1,9 @@ const app = require('vn-loopback/server/server'); -// Petición #1848 -xdescribe('agency getLanded()', () => { + +describe('agency getLanded()', () => { it('should return a landing date', async() => { const shipped = new Date(); + shipped.setDate(shipped.getDate() + 1); const addressFk = 121; const agencyModeFk = 7; const warehouseFk = 1; diff --git a/modules/agency/back/methods/agency/specs/getShipped.spec.js b/modules/agency/back/methods/agency/specs/getShipped.spec.js index 44519e181..8f79dab3b 100644 --- a/modules/agency/back/methods/agency/specs/getShipped.spec.js +++ b/modules/agency/back/methods/agency/specs/getShipped.spec.js @@ -1,10 +1,9 @@ const app = require('vn-loopback/server/server'); -// Petición #1848 - -xdescribe('agency getShipped()', () => { +describe('agency getShipped()', () => { it('should return a shipment date', async() => { const landed = new Date(); + landed.setDate(landed.getDate() + 1); const addressFk = 121; const agencyModeFk = 7; const warehouseFk = 1; diff --git a/modules/agency/back/methods/agency/specs/landsThatDay.spec.js b/modules/agency/back/methods/agency/specs/landsThatDay.spec.js index 98760d5fe..c6738afc3 100644 --- a/modules/agency/back/methods/agency/specs/landsThatDay.spec.js +++ b/modules/agency/back/methods/agency/specs/landsThatDay.spec.js @@ -1,14 +1,9 @@ const app = require('vn-loopback/server/server'); -// Petición #1848 -xdescribe('Agency landsThatDay()', () => { + +describe('Agency landsThatDay()', () => { const today = new Date(); it('should return a list of agencies that can land a shipment on a day for an address', async() => { - let filter = { - addressFk: 101, - landed: today, - }; - - let agencies = await app.models.Agency.landsThatDay(filter); + let agencies = await app.models.Agency.landsThatDay(101, today); expect(agencies.length).toBeGreaterThanOrEqual(3); }); diff --git a/modules/agency/back/methods/zone-calendar/removeByDate.js b/modules/agency/back/methods/zone-calendar/removeByDate.js deleted file mode 100644 index f7871502e..000000000 --- a/modules/agency/back/methods/zone-calendar/removeByDate.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = Self => { - Self.remoteMethod('removeByDate', { - description: 'Removes one or more delivery dates for a zone', - accessType: 'WRITE', - accepts: [{ - arg: 'zoneFk', - type: 'Number', - required: true, - }, - { - arg: 'dates', - type: ['Date'], - required: true, - }], - returns: { - type: 'object', - root: true - }, - http: { - path: `/removeByDate`, - verb: 'POST' - } - }); - - Self.removeByDate = (zoneFk, dates) => { - return Self.destroyAll({zoneFk, delivered: {inq: dates}}); - }; -}; diff --git a/modules/agency/back/methods/zone/clone.js b/modules/agency/back/methods/zone/clone.js index 3d0008a27..fcae904b5 100644 --- a/modules/agency/back/methods/zone/clone.js +++ b/modules/agency/back/methods/zone/clone.js @@ -31,7 +31,6 @@ module.exports = Self => { fields: [ 'name', 'hour', - 'warehouseFk', 'agencyModeFk', 'travelingDays', 'price', @@ -47,7 +46,8 @@ module.exports = Self => { }, options); // Find all original selected days - const calendarDays = await models.ZoneCalendar.find({ + const calendarDays = await models.ZoneEvent.find({ + fields: {id: false}, where: {zoneFk: id} }, options); @@ -62,7 +62,7 @@ module.exports = Self => { }); await models.ZoneIncluded.create(newIncludedGeo, options); - await models.ZoneCalendar.create(newCalendayDays, options); + await models.ZoneEvent.create(newCalendayDays, options); await tx.commit(); return newZone; diff --git a/modules/agency/back/methods/zone/editPrices.js b/modules/agency/back/methods/zone/editPrices.js deleted file mode 100644 index b0203dc4b..000000000 --- a/modules/agency/back/methods/zone/editPrices.js +++ /dev/null @@ -1,81 +0,0 @@ -const mergeFilters = require('vn-loopback/util/filter').mergeFilters; - -module.exports = Self => { - Self.remoteMethod('editPrices', { - description: 'Changes the price and bonus of a delivery day', - accessType: 'WRITE', - accepts: [{ - arg: 'id', - type: 'Number', - description: 'The zone id', - http: {source: 'path'} - }, - { - arg: 'delivered', - type: 'Date', - required: true, - }, - { - arg: 'price', - type: 'Number', - required: true, - }, - { - arg: 'bonus', - type: 'Number', - required: true, - }, - { - arg: 'option', - type: 'String', - required: true, - }], - returns: { - type: 'object', - root: true - }, - http: { - path: `/:id/editPrices`, - verb: 'POST' - } - }); - - Self.editPrices = async(id, delivered, price, bonus, option) => { - const models = Self.app.models; - - let filter = { - where: { - zoneFk: id - } - }; - - let where; - let shouldPropagate = true; - - if (option == 'Only this day') { - shouldPropagate = false; - where = {delivered}; - } else if (option == 'From this day') { - where = { - delivered: { - gte: delivered - } - }; - } - - filter = mergeFilters(filter, {where}); - - const days = await models.ZoneCalendar.find(filter); - const areAllFromSameZone = days.every(day => day.zoneFk === id); - - if (!areAllFromSameZone) - throw new UserError('All delivery days must belong to the same zone'); - - if (shouldPropagate) { - const zone = await models.Zone.findById(id); - zone.updateAttributes({price, bonus}); - } - - return models.ZoneCalendar.updateAll(filter.where, {price, bonus}); - }; -}; diff --git a/modules/agency/back/methods/zone/getEvents.js b/modules/agency/back/methods/zone/getEvents.js index b294222b5..1c70dfffd 100644 --- a/modules/agency/back/methods/zone/getEvents.js +++ b/modules/agency/back/methods/zone/getEvents.js @@ -4,19 +4,18 @@ module.exports = Self => { description: 'Returns delivery days for a postcode', accepts: [ { - arg: 'agencyModeFk', - type: 'Number', - description: 'The agency mode id', - required: true - }, { arg: 'provinceFk', type: 'Number', description: 'The province id', required: true }, { - arg: 'postCode', + arg: 'search', type: 'String', description: 'The postcode' + }, { + arg: 'agencyModeFk', + type: 'Number', + description: 'The agency mode id' } ], returns: { @@ -29,12 +28,11 @@ module.exports = Self => { } }); - Self.getEvents = async(agencyModeFk, provinceFk, postCode) => { + Self.getEvents = async(provinceFk, postCode, agencyModeFk) => { let [events, exclusions] = await Self.rawSql( `CALL zone_getEvents(?, ?, ?)`, - [agencyModeFk, provinceFk, postCode] + [provinceFk, postCode, agencyModeFk] ); - return {events, exclusions}; }; }; diff --git a/modules/agency/back/methods/zone/specs/editPrices.spec.js b/modules/agency/back/methods/zone/specs/editPrices.spec.js deleted file mode 100644 index a9a2457ad..000000000 --- a/modules/agency/back/methods/zone/specs/editPrices.spec.js +++ /dev/null @@ -1,81 +0,0 @@ -const app = require('vn-loopback/server/server'); -// Petición #1848 -xdescribe('agency editPrices()', () => { - const zoneId = 1; - let originalZone; - - beforeAll(async done => { - originalZone = await app.models.Zone.findById(zoneId); - done(); - }); - - afterAll(async done => { - await await app.models.ZoneCalendar.updateAll({zoneFk: zoneId}, { - price: originalZone.price, - bonus: originalZone.bonus - }); - done(); - }); - - it('should apply price and bonus for a selected day', async() => { - const delivered = new Date(); - delivered.setHours(0, 0, 0, 0); - await app.models.Zone.editPrices(zoneId, delivered, 4.00, 2.00, 'Only this day'); - - const editedDays = await app.models.ZoneCalendar.find({ - where: { - zoneFk: zoneId, - delivered: delivered - } - }); - const firstEditedDay = editedDays[0]; - - expect(editedDays.length).toEqual(1); - expect(firstEditedDay.price).toEqual(4.00); - expect(firstEditedDay.bonus).toEqual(2.00); - }); - - it('should apply price and bonus for all delivery days starting from selected day', async() => { - const delivered = new Date(); - delivered.setHours(0, 0, 0, 0); - await app.models.Zone.editPrices(1, delivered, 5.50, 1.00, 'From this day'); - - const editedDays = await app.models.ZoneCalendar.find({ - where: { - zoneFk: zoneId, - delivered: { - gte: delivered - } - } - }); - const firstEditedDay = editedDays[0]; - const lastEditedDay = editedDays[editedDays.length - 1]; - - expect(editedDays.length).toEqual(5); - expect(firstEditedDay.price).toEqual(5.50); - expect(firstEditedDay.bonus).toEqual(1.00); - expect(lastEditedDay.price).toEqual(5.50); - expect(lastEditedDay.bonus).toEqual(1.00); - }); - - it('should apply price and bonus for all delivery days', async() => { - const delivered = new Date(); - delivered.setHours(0, 0, 0, 0); - await app.models.Zone.editPrices(1, delivered, 7.00, 0.00, 'All days'); - - const editedDays = await app.models.ZoneCalendar.find({ - where: { - zoneFk: zoneId - } - }); - const firstEditedDay = editedDays[0]; - const lastEditedDay = editedDays[editedDays.length - 1]; - - expect(editedDays.length).toEqual(5); - expect(firstEditedDay.price).toEqual(7.00); - expect(firstEditedDay.bonus).toEqual(0.00); - expect(lastEditedDay.price).toEqual(7.00); - expect(lastEditedDay.bonus).toEqual(0.00); - }); -}); - diff --git a/modules/agency/back/model-config.json b/modules/agency/back/model-config.json index aded3eb5d..cfdbb83d3 100644 --- a/modules/agency/back/model-config.json +++ b/modules/agency/back/model-config.json @@ -20,9 +20,6 @@ "ZoneExclusion": { "dataSource": "vn" }, - "ZoneCalendar": { - "dataSource": "vn" - }, "ZoneIncluded": { "dataSource": "vn" }, diff --git a/modules/agency/back/models/zone-calendar.js b/modules/agency/back/models/zone-calendar.js deleted file mode 100644 index 871852882..000000000 --- a/modules/agency/back/models/zone-calendar.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = Self => { - require('../methods/zone-calendar/removeByDate')(Self); -}; diff --git a/modules/agency/back/models/zone-calendar.json b/modules/agency/back/models/zone-calendar.json deleted file mode 100644 index bed9a8011..000000000 --- a/modules/agency/back/models/zone-calendar.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "ZoneCalendar", - "base": "VnModel", - "options": { - "mysql": { - "table": "zoneCalendar" - } - }, - "properties": { - "zoneFk": { - "id": true, - "type": "Number" - }, - "delivered": { - "id": true, - "type": "Date" - }, - "price": { - "type": "Number" - }, - "bonus": { - "type": "Number" - } - }, - "relations": { - "zone": { - "type": "belongsTo", - "model": "Zone", - "foreignKey": "zoneFk" - } - } -} \ No newline at end of file diff --git a/modules/agency/back/models/zone-event.js b/modules/agency/back/models/zone-event.js index 5b1c183fd..6af031a23 100644 --- a/modules/agency/back/models/zone-event.js +++ b/modules/agency/back/models/zone-event.js @@ -1,9 +1,35 @@ module.exports = Self => { - function rangeValid(err) { - if (this.from && this.to && this.from >= this.to) + Self.validate('range', function(err) { + if (this.type == 'range' + && !this.started + && !this.ended) err(); - } - Self.validate('rangeValid', rangeValid, { + }, { + message: `You should specify at least a start or end date` + }); + + Self.validate('validRange', function(err) { + if (this.type == 'range' + && this.started + && this.ended + && this.started >= this.ended) + err(); + }, { message: `Start date should be lower than end date` }); + + Self.validate('dated', function(err) { + if (this.type == 'day' && !this.dated) + err(); + }, { + message: `You should specify a date` + }); + + Self.validate('weekDays', function(err) { + if (['range', 'indefinitely'].indexOf(this.type) !== -1 + && !this.weekDays) + err(); + }, { + message: `You should mark at least one week day` + }); }; diff --git a/modules/agency/back/models/zone-event.json b/modules/agency/back/models/zone-event.json index 822328dc6..0d46b1fe2 100644 --- a/modules/agency/back/models/zone-event.json +++ b/modules/agency/back/models/zone-event.json @@ -15,10 +15,16 @@ "id": true, "type": "Number" }, - "from": { + "type": { + "type": "String" + }, + "dated": { "type": "Date" }, - "to": { + "started": { + "type": "Date" + }, + "ended": { "type": "Date" }, "weekDays": { diff --git a/modules/agency/back/models/zone-exclusion.json b/modules/agency/back/models/zone-exclusion.json index 79e2f5aae..f1c80debd 100644 --- a/modules/agency/back/models/zone-exclusion.json +++ b/modules/agency/back/models/zone-exclusion.json @@ -11,7 +11,7 @@ "id": true, "type": "Number" }, - "day": { + "dated": { "type": "Date", "required": true } diff --git a/modules/agency/back/models/zone.js b/modules/agency/back/models/zone.js index 1f8b0a675..0c3ac24f6 100644 --- a/modules/agency/back/models/zone.js +++ b/modules/agency/back/models/zone.js @@ -1,6 +1,5 @@ module.exports = Self => { require('../methods/zone/clone')(Self); - require('../methods/zone/editPrices')(Self); require('../methods/zone/getLeaves')(Self); require('../methods/zone/getEvents')(Self); require('../methods/zone/toggleIsIncluded')(Self); diff --git a/modules/agency/front/basic-data/index.html b/modules/agency/front/basic-data/index.html index b0f94ddc0..ec5cc3b20 100644 --- a/modules/agency/front/basic-data/index.html +++ b/modules/agency/front/basic-data/index.html @@ -84,14 +84,17 @@ - - + + diff --git a/modules/agency/front/calendar/index.html b/modules/agency/front/calendar/index.html index 9a5f97269..b1277cbe5 100644 --- a/modules/agency/front/calendar/index.html +++ b/modules/agency/front/calendar/index.html @@ -5,7 +5,12 @@ ng-click="$ctrl.step(-1)" class="flat"> - {{$ctrl.firstDay | date:'MMMM yyyy'}} - {{$ctrl.lastDay | date:'MMMM yyyy'}} +
        + {{$ctrl.firstDay | date:'MMMM'}} + {{$ctrl.firstDay | date:'yyyy'}} - + {{$ctrl.lastDay | date:'MMMM'}} + {{$ctrl.lastDay | date:'yyyy'}} +
        = from && stamp <= to - && event.wdays[wday]; - } else if (event.from) - match = from == stamp; - else - match = event.wdays[wday]; + switch (event.type) { + case 'day': + match = event.dated == stamp; + break; + default: + match = event.wdays[wday] + && (!event.started || stamp >= event.started) + && (!event.ended || stamp <= event.ended); + break; + } if (match) dayEvents.push(event); } } - let exclusion = exclusionsMap[stamp]; + let exclusion = this.exclusionsMap[stamp]; if (dayEvents.length || exclusion) { let dayData = {}; @@ -152,6 +156,7 @@ class Controller extends Component { return dayData && dayData.exclusion ? 'excluded' : ''; } } +Controller.$inject = ['$element', '$scope', 'vnWeekDays']; ngModule.component('vnZoneCalendar', { template: require('./index.html'), diff --git a/modules/agency/front/card/index.html b/modules/agency/front/card/index.html index 1fd22809a..ae6a7f10a 100644 --- a/modules/agency/front/card/index.html +++ b/modules/agency/front/card/index.html @@ -1,5 +1,5 @@ - + - - -
        + + + diff --git a/modules/agency/front/card/index.js b/modules/agency/front/card/index.js index 7c53e9294..4e8ac7e8c 100644 --- a/modules/agency/front/card/index.js +++ b/modules/agency/front/card/index.js @@ -1,38 +1,21 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $stateParams) { - this.$http = $http; - this.$stateParams = $stateParams; - } - - $onInit() { - this.getCard(); - } - - getCard() { +class Controller extends ModuleCard { + reload() { let filter = { include: { relation: 'agencyMode', scope: {fields: ['name']} } }; - let json = encodeURIComponent(JSON.stringify(filter)); - let query = `Zones/${this.$stateParams.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data) - this.zone = res.data; - }); - } - reload() { - this.getCard(); + this.$http.get(`Zones/${this.$params.id}`, {filter}) + .then(res => this.zone = res.data); } } -Controller.$inject = ['$http', '$stateParams']; - -ngModule.component('vnZoneCard', { +ngModule.vnComponent('vnZoneCard', { template: require('./index.html'), controller: Controller }); diff --git a/modules/agency/front/card/index.spec.js b/modules/agency/front/card/index.spec.js index 190b77d0d..cc53fe946 100644 --- a/modules/agency/front/card/index.spec.js +++ b/modules/agency/front/card/index.spec.js @@ -1,35 +1,26 @@ import './index.js'; describe('Agency Component vnZoneCard', () => { - let $scope; let controller; let $httpBackend; - let $stateParams; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('agency')); - beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $stateParams = {id: 1}; - controller = $componentController('vnZoneCard', {$scope, $stateParams}); + + let $element = angular.element('
        '); + controller = $componentController('vnZoneCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Zones/:id').respond(data); })); - describe('getCard()', () => { - it(`should make a query and define zone property`, () => { - let filter = { - include: { - relation: 'agencyMode', - scope: {fields: ['name']} - } - }; - let json = encodeURIComponent(JSON.stringify(filter)); - $httpBackend.expectGET(`Zones/1?filter=${json}`).respond({id: 1}); - controller.getCard(); - $httpBackend.flush(); + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.zone).toEqual({id: 1}); - }); + expect(controller.zone).toEqual(data); }); }); - diff --git a/modules/agency/front/create/index.html b/modules/agency/front/create/index.html index 716d5e50c..332e8a9b8 100644 --- a/modules/agency/front/create/index.html +++ b/modules/agency/front/create/index.html @@ -5,79 +5,82 @@ form="form" save="post"> -
        -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        -
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        diff --git a/modules/agency/front/delivery-days/index.html b/modules/agency/front/delivery-days/index.html index 286721068..2b8a0e423 100644 --- a/modules/agency/front/delivery-days/index.html +++ b/modules/agency/front/delivery-days/index.html @@ -1,43 +1,33 @@
        -
        - - - - - - - - -
        {{name}}
        -
        - {{country.country}} -
        -
        -
        - - -
        -
        - - - -
        + data="data">
        + +
        + + +
        {{name}}
        +
        + {{country.country}} +
        +
        +
        + + + + + +
        +
        diff --git a/modules/agency/front/delivery-days/index.js b/modules/agency/front/delivery-days/index.js index cebfdbf7e..e9c885a13 100644 --- a/modules/agency/front/delivery-days/index.js +++ b/modules/agency/front/delivery-days/index.js @@ -1,20 +1,17 @@ import ngModule from '../module'; +import Section from 'salix/components/section'; import './style.scss'; -class Controller { - constructor($, $http) { - Object.assign(this, { - $, - $http - }); +class Controller extends Section { + $onInit() { + this.$.params = {}; } onSubmit() { - this.$http.get(`Zones/getEvents`, {params: this.params}) + this.$http.get(`Zones/getEvents`, {params: this.$.params}) .then(res => this.$.data = res.data); } } -Controller.$inject = ['$scope', '$http']; ngModule.component('vnZoneDeliveryDays', { template: require('./index.html'), diff --git a/modules/agency/front/delivery-days/style.scss b/modules/agency/front/delivery-days/style.scss index 1db8edfbf..79f95a3dc 100644 --- a/modules/agency/front/delivery-days/style.scss +++ b/modules/agency/front/delivery-days/style.scss @@ -9,4 +9,8 @@ vn-zone-delivery-days { min-width: 16.5em; } } + form { + display: flex; + flex-direction: column; + } } \ No newline at end of file diff --git a/modules/agency/front/edit/index.html b/modules/agency/front/edit/index.html deleted file mode 100644 index f66c289ee..000000000 --- a/modules/agency/front/edit/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        diff --git a/modules/agency/front/events/index.html b/modules/agency/front/events/index.html index 4764ded3c..1b8c1e871 100644 --- a/modules/agency/front/events/index.html +++ b/modules/agency/front/events/index.html @@ -1,11 +1,9 @@ -
        - - -
        + +
        -
        + is-loading="!data.events"> + @@ -99,40 +94,40 @@ + ng-model="$ctrl.selected.dated"> + ng-if="$ctrl.selected.type == 'range'"> + ng-model="$ctrl.selected.started"> + ng-model="$ctrl.selected.ended"> diff --git a/modules/agency/front/events/index.js b/modules/agency/front/events/index.js index f7e689afe..b6352802a 100644 --- a/modules/agency/front/events/index.js +++ b/modules/agency/front/events/index.js @@ -1,7 +1,7 @@ import ngModule from '../module'; -import Component from 'core/lib/component'; +import Section from 'salix/components/section'; -class Controller extends Component { +class Controller extends Section { constructor($element, $, vnWeekDays) { super($element, $); this.vnWeekDays = vnWeekDays; @@ -27,13 +27,13 @@ class Controller extends Component { formatWdays(weekDays) { if (!weekDays) return; - let abrWdays = []; - for (let wday of weekDays.split(',')) - abrWdays.push(this.vnWeekDays.map[wday].localeAbr); + let abrWdays = weekDays + .split(',') + .map(wday => this.vnWeekDays.map[wday].localeAbr); return abrWdays.length < 7 ? abrWdays.join(', ') - : this._('Everyday'); + : this.$t('Everyday'); } onSelection(days, type, weekday, data) { @@ -66,37 +66,28 @@ class Controller extends Component { edit(row) { this.isNew = false; - - if (row.from && !row.to) - this.eventType = 'day'; - else if (!row.from) - this.eventType = 'indefinitely'; - else - this.eventType = 'range'; - this.selected = angular.copy(row); - this.selected.wdays = {}; - - if (row.weekDays) { - let weekDays = row.weekDays.split(','); - for (let day of weekDays) - this.selected.wdays[day] = true; - } - + this.selected.wdays = this.vnWeekDays.fromSet(row.weekDays); this.$.dialog.show(); } create(days, type, weekday) { this.isNew = true; - this.eventType = type == 'day' ? 'day' : 'indefinitely'; if (type == 'weekday') { let wdays = []; - let code = this.vnWeekDays.days[weekday].code; - wdays[code] = true; - this.selected = {wdays}; - } else - this.selected = {from: days[0]}; + wdays[weekday] = true; + + this.selected = { + type: 'indefinitely', + wdays + }; + } else { + this.selected = { + type: 'day', + dated: days[0] + }; + } this.$.dialog.show(); } @@ -105,24 +96,18 @@ class Controller extends Component { switch (response) { case 'accept': { let selected = this.selected; + let type = selected.type; - if (this.eventType == 'indefinitely') { - selected.from = null; - selected.to = null; - } + selected.weekDays = this.vnWeekDays.toSet(selected.wdays); - if (this.eventType != 'day') { - let weekDays = []; - - for (let wday in selected.wdays) { - if (selected.wdays[wday]) - weekDays.push(wday); - } - - selected.weekDays = weekDays.join(','); - } else { - selected.to = null; + if (type == 'day') selected.weekDays = ''; + else + selected.dated = null; + + if (type != 'range') { + selected.started = null; + selected.ended = null; } let req; @@ -138,7 +123,7 @@ class Controller extends Component { this.refresh(); }); } - case 'DELETE': + case 'delete': return this.onDelete(this.selected.id) .then(response => response == 'accept'); } @@ -162,8 +147,8 @@ class Controller extends Component { } exclusionCreate(days) { - let exclusions = days.map(day => { - return {day}; + let exclusions = days.map(dated => { + return {dated}; }); this.$http.post(this.exclusionsPath, exclusions) diff --git a/modules/agency/front/index/index.html b/modules/agency/front/index/index.html index 18229cd34..cb820b66a 100644 --- a/modules/agency/front/index/index.html +++ b/modules/agency/front/index/index.html @@ -6,63 +6,56 @@ data="zones" auto-load="true"> -
        - - - + + + + + + + + Id + Name + Agency + Closing + Price + + + + + + {{::zone.id}} + {{::zone.name}} + {{::zone.agencyMode.name}} + {{::zone.hour | date: 'HH:mm'}} + {{::zone.price | currency: 'EUR':2}} + + + + + + + + + + + - - - - - - Id - Name - Agency - Closing - Price - - - - - - {{::zone.id}} - {{::zone.name}} - {{::zone.agencyMode.name}} - {{::zone.hour | date: 'HH:mm'}} - {{::zone.price | currency: 'EUR':2}} - - - - - - - - - - - - - -
        + diff --git a/modules/agency/front/location-search-panel/index.html b/modules/agency/front/location-search-panel/index.html deleted file mode 100644 index 57f661ddf..000000000 --- a/modules/agency/front/location-search-panel/index.html +++ /dev/null @@ -1,40 +0,0 @@ -
        -
        - - - - - - - - - - - - - - - - - -
        -
        \ No newline at end of file diff --git a/modules/agency/front/location-search-panel/index.js b/modules/agency/front/location-search-panel/index.js deleted file mode 100644 index c877bc83e..000000000 --- a/modules/agency/front/location-search-panel/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import ngModule from '../module'; -import SearchPanel from 'core/components/searchbar/search-panel'; - -ngModule.component('vnLocationSearchPanel', { - template: require('./index.html'), - controller: SearchPanel -}); diff --git a/modules/agency/front/location/index.html b/modules/agency/front/location/index.html index 6882ccc5b..4b3523a1f 100644 --- a/modules/agency/front/location/index.html +++ b/modules/agency/front/location/index.html @@ -3,19 +3,20 @@ url="Zones/{{$ctrl.$params.id}}/getLeaves" filter="::$ctrl.filter"> + + + + +
        - - - - - - { const data = this.$.model.data; @@ -12,9 +16,8 @@ class Controller extends Component { onFetch(item) { const params = item ? {parentId: item.id} : null; - return this.$.model.applyFilter({}, params).then(() => { - return this.$.model.data; - }); + return this.$.model.applyFilter({}, params) + .then(() => this.$.model.data); } onSort(a, b) { diff --git a/modules/agency/front/location/style.scss b/modules/agency/front/location/style.scss index d03701e42..4972a5d29 100644 --- a/modules/agency/front/location/style.scss +++ b/modules/agency/front/location/style.scss @@ -2,13 +2,13 @@ vn-treeview-child { .content > .vn-check:not(.indeterminate) { - color: $color-main; + color: $color-button; & > .btn { - border-color: $color-main; + border-color: $color-button; } } .content > .vn-check.checked { - color: $color-main; + color: $color-button; } } \ No newline at end of file diff --git a/modules/agency/front/main/index.html b/modules/agency/front/main/index.html index d695d74aa..230bf7f6d 100644 --- a/modules/agency/front/main/index.html +++ b/modules/agency/front/main/index.html @@ -1,14 +1,11 @@ - - - - -
        -
        - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/modules/agency/front/main/index.js b/modules/agency/front/main/index.js index 223b78caa..3e8890310 100644 --- a/modules/agency/front/main/index.js +++ b/modules/agency/front/main/index.js @@ -1,6 +1,9 @@ import ngModule from '../module'; -import './style.scss'; +import ModuleMain from 'salix/components/module-main'; -ngModule.component('vnZone', { +export default class Zone extends ModuleMain {} + +ngModule.vnComponent('vnZone', { + controller: Zone, template: require('./index.html') }); diff --git a/modules/agency/front/main/style.scss b/modules/agency/front/main/style.scss deleted file mode 100644 index 21e4fc40c..000000000 --- a/modules/agency/front/main/style.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import "effects"; - -vn-zone { - ul.menu { - list-style-type: none; - padding: 0; - padding-top: $spacing-md; - margin: 0; - font-size: inherit; - - & > li > a { - @extend %clickable; - display: block; - color: inherit; - padding: .6em 2em; - } - } -} \ No newline at end of file diff --git a/modules/agency/front/routes.json b/modules/agency/front/routes.json index 26d619172..7c5b1338d 100644 --- a/modules/agency/front/routes.json +++ b/modules/agency/front/routes.json @@ -4,12 +4,18 @@ "icon" : "icon-delivery", "validations" : true, "dependencies": ["worker"], - "menu": [ - {"state": "zone.card.basicData", "icon": "settings"}, - {"state": "zone.card.location", "icon": "my_location"}, - {"state": "zone.card.warehouses", "icon": "home"}, - {"state": "zone.card.events", "icon": "today"} - ], + "menus": { + "main": [ + {"state": "zone.index", "icon": "icon-delivery"}, + {"state": "zone.deliveryDays", "icon": "today"} + ], + "card": [ + {"state": "zone.card.basicData", "icon": "settings"}, + {"state": "zone.card.location", "icon": "my_location"}, + {"state": "zone.card.warehouses", "icon": "home"}, + {"state": "zone.card.events", "icon": "today"} + ] + }, "routes": [ { "url": "/zone", @@ -23,7 +29,7 @@ "component": "vn-zone-index", "description": "Zones" }, { - "url": "/delivery-days", + "url": "/delivery-days?q", "state": "zone.deliveryDays", "component": "vn-zone-delivery-days", "description": "Delivery days" diff --git a/modules/agency/front/search-panel/index.html b/modules/agency/front/search-panel/index.html index 1fd07cbf5..a97428192 100644 --- a/modules/agency/front/search-panel/index.html +++ b/modules/agency/front/search-panel/index.html @@ -22,8 +22,8 @@ label="Agency" ng-model="filter.agencyModeFk" url="AgencyModes/isActive" - show-field="name" - value-field="id"> + value-field="id" + show-field="name"> diff --git a/modules/agency/front/summary/index.html b/modules/agency/front/summary/index.html index f0336f0dc..664513287 100644 --- a/modules/agency/front/summary/index.html +++ b/modules/agency/front/summary/index.html @@ -1,16 +1,21 @@ + + -
        {{$ctrl.summary.name}}
        +
        #{{$ctrl.summary.id}} - {{$ctrl.summary.name}}
        - - - - + + + + - - - - @@ -32,4 +31,21 @@ + + +

        Warehouses

        + + + + Name + + + + + {{zoneWarehouse.warehouse.name}} + + + +
        +
        \ No newline at end of file diff --git a/modules/agency/front/summary/index.js b/modules/agency/front/summary/index.js index 992e23937..66d305a4a 100644 --- a/modules/agency/front/summary/index.js +++ b/modules/agency/front/summary/index.js @@ -1,10 +1,7 @@ import ngModule from '../module'; +import Component from 'core/lib/component'; -class Controller { - constructor($http) { - this.$http = $http; - } - +class Controller extends Component { get zone() { return this._zone; } @@ -30,8 +27,6 @@ class Controller { } } -Controller.$inject = ['$http']; - ngModule.component('vnZoneSummary', { template: require('./index.html'), controller: Controller, diff --git a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js index 14655e640..5fecf8bcd 100644 --- a/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js +++ b/modules/claim/back/methods/claim-beginning/importToNewRefundTicket.spec.js @@ -1,6 +1,6 @@ const app = require('vn-loopback/server/server'); -// Petición #1848 -xdescribe('claimBeginning', () => { + +describe('claimBeginning', () => { let ticket; let refundTicketSales; let salesInsertedInClaimEnd; diff --git a/modules/claim/front/basic-data/index.html b/modules/claim/front/basic-data/index.html index cd8cade9f..9be04cb57 100644 --- a/modules/claim/front/basic-data/index.html +++ b/modules/claim/front/basic-data/index.html @@ -5,7 +5,7 @@ url="Claims/{{$ctrl.$stateParams.id}}/updateClaim" save="post"> -
        + diff --git a/modules/claim/front/card/index.html b/modules/claim/front/card/index.html index 3a445f0b1..1db6b38db 100644 --- a/modules/claim/front/card/index.html +++ b/modules/claim/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/claim/front/card/index.js b/modules/claim/front/card/index.js index 0a641ce89..ccd78a271 100644 --- a/modules/claim/front/card/index.js +++ b/modules/claim/front/card/index.js @@ -1,12 +1,12 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $state) { - this.$http = $http; - this.$state = $state; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { include: [ - {relation: 'worker', + { + relation: 'worker', scope: { fields: ['userFk'], include: { @@ -16,8 +16,7 @@ class Controller { } } } - }, - { + }, { relation: 'ticket', scope: { fields: ['agencyModeFk'], @@ -25,14 +24,12 @@ class Controller { relation: 'agencyMode' } } - }, - { + }, { relation: 'claimState', scope: { fields: ['id', 'description'] } - }, - { + }, { relation: 'client', scope: { fields: ['salesPersonFk', 'name', 'email'], @@ -53,29 +50,13 @@ class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - let json = encodeURIComponent(JSON.stringify(this.filter)); - let query = `Claims/${this.$state.params.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data) - this.claim = res.data; - }); - } - - reload() { - this.getCard(); + this.$http.get(`Claims/${this.$params.id}`, {filter}) + .then(res => this.claim = res.data); } } -Controller.$inject = ['$http', '$state']; - -ngModule.component('vnClaimCard', { +ngModule.vnComponent('vnClaimCard', { template: require('./index.html'), controller: Controller }); diff --git a/modules/claim/front/card/index.spec.js b/modules/claim/front/card/index.spec.js index dfd106e89..1b2344925 100644 --- a/modules/claim/front/card/index.spec.js +++ b/modules/claim/front/card/index.spec.js @@ -2,29 +2,27 @@ import './index.js'; describe('Claim', () => { describe('Component vnClaimCard', () => { - let $scope; let controller; let $httpBackend; - let $state; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('claim')); - beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $state = {params: {id: 1}}; - controller = $componentController('vnClaimCard', {$scope, $state}); + + let $element = angular.element('
        '); + controller = $componentController('vnClaimCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Claims/:id').respond(data); })); - describe('getCard()', () => { - it(`should make a query and save the data in claim`, () => { - let json = encodeURIComponent(JSON.stringify(controller.filter)); - $httpBackend.expectGET(`Claims/${controller.$state.params.id}?filter=${json}`).respond({id: 1}); - controller.getCard(); - $httpBackend.flush(); + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.claim).toEqual({id: 1}); - }); + expect(controller.claim).toEqual(data); }); }); }); diff --git a/modules/claim/front/development/index.html b/modules/claim/front/development/index.html index 281eb0188..cf777f31a 100644 --- a/modules/claim/front/development/index.html +++ b/modules/claim/front/development/index.html @@ -36,7 +36,7 @@ data="claimDevelopments" form="form"> - + diff --git a/modules/claim/front/dms/index/index.html b/modules/claim/front/dms/index/index.html index ca12c2a05..e3cd5da5b 100644 --- a/modules/claim/front/dms/index/index.html +++ b/modules/claim/front/dms/index/index.html @@ -16,7 +16,7 @@
        + zoom-image="/api/dms/{{::photo.dmsFk}}/downloadFile?access_token={{::$ctrl.accessToken}}">
        - + diff --git a/modules/claim/front/main/index.html b/modules/claim/front/main/index.html new file mode 100644 index 000000000..e733162b1 --- /dev/null +++ b/modules/claim/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/claim/front/main/index.js b/modules/claim/front/main/index.js new file mode 100644 index 000000000..77b051897 --- /dev/null +++ b/modules/claim/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Claim extends ModuleMain {} + +ngModule.vnComponent('vnClaim', { + controller: Claim, + template: require('./index.html') +}); diff --git a/modules/claim/front/routes.json b/modules/claim/front/routes.json index bda7da74b..80bc77c49 100644 --- a/modules/claim/front/routes.json +++ b/modules/claim/front/routes.json @@ -4,13 +4,18 @@ "icon": "icon-claims", "validations": true, "dependencies": ["worker", "item", "client"], - "menu": [ - {"state": "claim.card.basicData", "icon": "settings"}, - {"state": "claim.card.detail", "icon": "icon-details"}, - {"state": "claim.card.dms.index", "icon": "image"}, - {"state": "claim.card.development", "icon": "icon-traceability"}, - {"state": "claim.card.action", "icon": "icon-actions"} - ], + "menus": { + "main": [ + {"state": "claim.index", "icon": "icon-claims"} + ], + "card": [ + {"state": "claim.card.basicData", "icon": "settings"}, + {"state": "claim.card.detail", "icon": "icon-details"}, + {"state": "claim.card.dms.index", "icon": "image"}, + {"state": "claim.card.development", "icon": "icon-traceability"}, + {"state": "claim.card.action", "icon": "icon-actions"} + ] + }, "keybindings": [ {"key": "r", "state": "claim.index"} ], @@ -19,7 +24,7 @@ "url": "/claim", "state": "claim", "abstract": true, - "component": "ui-view", + "component": "vn-claim", "description": "Claims" }, { "url": "/index?q", diff --git a/modules/client/front/address/create/index.html b/modules/client/front/address/create/index.html index 3e9c4ab41..43f6e3589 100644 --- a/modules/client/front/address/create/index.html +++ b/modules/client/front/address/create/index.html @@ -14,7 +14,7 @@ data="observations" auto-load="true"> - + - + - + - - - - -
        - + + + + + diff --git a/modules/client/front/card/index.js b/modules/client/front/card/index.js index c785644d9..5aacc83ea 100644 --- a/modules/client/front/card/index.js +++ b/modules/client/front/card/index.js @@ -1,28 +1,12 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($scope, $stateParams, $http) { - this.$scope = $scope; - this.$http = $http; - this.$stateParams = $stateParams; - this.client = null; - } - - $onInit() { - this.getCard(); - } - - getCard() { - this.$http.get(`Clients/${this.$stateParams.id}/getCard`).then(response => { - this.client = response.data; - }); - } - +export default class Controller extends ModuleCard { reload() { - this.getCard(); + this.$http.get(`Clients/${this.$params.id}/getCard`) + .then(res => this.client = res.data); } } -Controller.$inject = ['$scope', '$stateParams', '$http']; ngModule.component('vnClientCard', { template: require('./index.html'), diff --git a/modules/client/front/card/index.spec.js b/modules/client/front/card/index.spec.js index 502e04677..72272fe89 100644 --- a/modules/client/front/card/index.spec.js +++ b/modules/client/front/card/index.spec.js @@ -2,21 +2,27 @@ import './index'; describe('Client', () => { describe('Component vnClientCard', () => { - let $componentController; - let $scope; let controller; + let $httpBackend; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('client')); - beforeEach(angular.mock.inject((_$componentController_, $rootScope) => { - $componentController = _$componentController_; - $scope = $rootScope; - controller = $componentController('vnClientCard', {$scope: $scope}); + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { + $httpBackend = _$httpBackend_; + + let $element = angular.element('
        '); + controller = $componentController('vnClientCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Clients/:id/getCard').respond(data); })); - it('should define and set client property to null in the module instance', () => { - expect(controller.client).toBeDefined(); - expect(controller.client).toBe(null); + it('should request data and set it on the controller', () => { + controller.$onInit(); + $httpBackend.flush(); + + expect(controller.client).toEqual(data); }); }); }); diff --git a/modules/client/front/contact/index.html b/modules/client/front/contact/index.html index fa58f27d4..2cefdd86d 100644 --- a/modules/client/front/contact/index.html +++ b/modules/client/front/contact/index.html @@ -11,7 +11,7 @@ data="contacts" form="form"> - + -
        - - - - - - - {{firstName}} {{lastName}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        \ No newline at end of file +
        + + + + + + {{firstName}} {{lastName}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + + + \ No newline at end of file diff --git a/modules/client/front/credit-insurance/create/index.html b/modules/client/front/credit-insurance/create/index.html index 5dbe258b6..d1dc081b7 100644 --- a/modules/client/front/credit-insurance/create/index.html +++ b/modules/client/front/credit-insurance/create/index.html @@ -1,4 +1,4 @@ -
        + - + - + -
        +
        diff --git a/modules/client/front/dms/edit/index.html b/modules/client/front/dms/edit/index.html index a63a58c44..dbc2e0ed1 100644 --- a/modules/client/front/dms/edit/index.html +++ b/modules/client/front/dms/edit/index.html @@ -7,7 +7,7 @@ ng-submit="$ctrl.onSubmit()" class="vn-ma-md" enctype="multipart/form-data"> -
        +
        - + - + - + { - let $scope; + let $state; let controller; beforeEach(ngModule('client')); - beforeEach(angular.mock.inject(($componentController, $rootScope, $state) => { - $scope = $rootScope.$new(); - controller = $componentController('vnClientIndex', {$scope, $state}); + beforeEach(angular.mock.inject(($componentController, _$state_) => { + $state = _$state_; + controller = $componentController('vnClientIndex'); })); describe('filterTickets()', () => { it('should navigate to the ticket index using params as filter', () => { const client = {id: 101}; - const event = {preventDefault: () => {}}; - spyOn(event, 'preventDefault'); - spyOn(controller.$state, 'go'); + const event = new MouseEvent('click', {cancelable: true}); + spyOn($state, 'go'); controller.filterTickets(client, event); - expect(event.preventDefault).toHaveBeenCalledWith(); - expect(controller.$state.go).toHaveBeenCalledWith('ticket.index', jasmine.any(Object)); + expect($state.go).toHaveBeenCalledWith('ticket.index', jasmine.any(Object)); }); }); }); diff --git a/modules/client/front/main/index.html b/modules/client/front/main/index.html new file mode 100644 index 000000000..1ae1d211b --- /dev/null +++ b/modules/client/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/client/front/main/index.js b/modules/client/front/main/index.js new file mode 100644 index 000000000..890165a19 --- /dev/null +++ b/modules/client/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Client extends ModuleMain {} + +ngModule.vnComponent('vnClient', { + controller: Client, + template: require('./index.html') +}); diff --git a/modules/client/front/note/create/index.html b/modules/client/front/note/create/index.html index 00a18edba..c9c351199 100644 --- a/modules/client/front/note/create/index.html +++ b/modules/client/front/note/create/index.html @@ -6,7 +6,7 @@ save="post" form="form"> - + - + - + - + - - - - -
        - + + + + + diff --git a/modules/invoiceOut/front/card/index.js b/modules/invoiceOut/front/card/index.js index c1a934924..0ad91f4c2 100644 --- a/modules/invoiceOut/front/card/index.js +++ b/modules/invoiceOut/front/card/index.js @@ -1,11 +1,9 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($stateParams, $http) { - this.$http = $http; - this.$stateParams = $stateParams; - this.route = null; - this.filter = { +class Controller extends ModuleCard { + reload() { + const filter = { fields: [ 'id', 'ref', @@ -28,24 +26,11 @@ export default class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - const params = {filter: this.filter}; - this.$http.get(`InvoiceOuts/${this.$stateParams.id}`, {params}).then(response => { - this.invoiceOut = response.data; - }); - } - - reload() { - this.getCard(); + this.$http.get(`InvoiceOuts/${this.$params.id}`, {filter}) + .then(res => this.invoiceOut = res.data); } } -Controller.$inject = ['$stateParams', '$http']; ngModule.component('vnInvoiceOutCard', { template: require('./index.html'), diff --git a/modules/invoiceOut/front/index.js b/modules/invoiceOut/front/index.js index 239bfd812..9843e188b 100644 --- a/modules/invoiceOut/front/index.js +++ b/modules/invoiceOut/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './summary'; diff --git a/modules/invoiceOut/front/index/index.html b/modules/invoiceOut/front/index/index.html index 1a74b28b4..c798b8bc6 100644 --- a/modules/invoiceOut/front/index/index.html +++ b/modules/invoiceOut/front/index/index.html @@ -5,72 +5,65 @@ data="invoiceOuts" order="issued DESC"> -
        + diff --git a/modules/invoiceOut/front/main/index.html b/modules/invoiceOut/front/main/index.html new file mode 100644 index 000000000..18e72ed5e --- /dev/null +++ b/modules/invoiceOut/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/invoiceOut/front/main/index.js b/modules/invoiceOut/front/main/index.js new file mode 100644 index 000000000..ad37e9c4b --- /dev/null +++ b/modules/invoiceOut/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class InvoiceOut extends ModuleMain {} + +ngModule.vnComponent('vnInvoiceOut', { + controller: InvoiceOut, + template: require('./index.html') +}); diff --git a/modules/invoiceOut/front/routes.json b/modules/invoiceOut/front/routes.json index eb278ebd8..8f828a634 100644 --- a/modules/invoiceOut/front/routes.json +++ b/modules/invoiceOut/front/routes.json @@ -4,12 +4,17 @@ "icon": "icon-invoices", "validations" : true, "dependencies": ["worker", "client", "ticket"], + "menus": { + "main": [ + {"state": "invoiceOut.index", "icon": "icon-invoices"} + ] + }, "routes": [ { "url": "/invoice-out", "state": "invoiceOut", "abstract": true, - "component": "ui-view", + "component": "vn-invoice-out", "description": "InvoiceOut", "acl": ["employee"] }, diff --git a/modules/item/front/barcode/index.html b/modules/item/front/barcode/index.html index 316fff80f..dba745d28 100644 --- a/modules/item/front/barcode/index.html +++ b/modules/item/front/barcode/index.html @@ -11,7 +11,7 @@ data="barcodes" form="form"> - + - + - + - - - - -
        - + + + + + diff --git a/modules/item/front/card/index.js b/modules/item/front/card/index.js index c5adb14b8..12167500f 100644 --- a/modules/item/front/card/index.js +++ b/modules/item/front/card/index.js @@ -1,29 +1,13 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $stateParams) { - this.$http = $http; - this.$stateParams = $stateParams; - this.item = null; - } - - $onInit() { - this.getCard(); - } - - getCard() { - this.$http.get(`Items/${this.$stateParams.id}/getCard`).then(response => { - this.item = response.data; - }); - } - +class Controller extends ModuleCard { reload() { - this.getCard(); + this.$http.get(`Items/${this.$params.id}/getCard`) + .then(res => this.item = res.data); } } -Controller.$inject = ['$http', '$stateParams']; - ngModule.component('vnItemCard', { template: require('./index.html'), controller: Controller diff --git a/modules/item/front/card/index.spec.js b/modules/item/front/card/index.spec.js index 1db1db140..c0ea80e69 100644 --- a/modules/item/front/card/index.spec.js +++ b/modules/item/front/card/index.spec.js @@ -2,30 +2,27 @@ import './index.js'; describe('Item', () => { describe('Component vnItemCard', () => { - let $httpBackend; - let $stateParams; let controller; + let $httpBackend; + let data = {id: 1, name: 'fooName'}; beforeEach(ngModule('item')); - beforeEach(angular.mock.inject(($componentController, _$stateParams_, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $stateParams = { - id: 123 - }; - controller = $componentController('vnItemCard', {$stateParams}); + + let $element = angular.element('
        '); + controller = $componentController('vnItemCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Items/:id/getCard').respond(data); })); - describe('getCard()', () => { - it('should request to set the item into the controller', () => { - let response = {id: 123}; - $httpBackend.when('GET', `Items/123/getCard`).respond(response); - $httpBackend.expect('GET', `Items/123/getCard`); - controller.getCard(); - $httpBackend.flush(); + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.item).toEqual(response); - }); + expect(controller.item).toEqual(data); }); }); }); diff --git a/modules/item/front/create/index.html b/modules/item/front/create/index.html index 8d22d7145..0c00aa8e9 100644 --- a/modules/item/front/create/index.html +++ b/modules/item/front/create/index.html @@ -5,61 +5,59 @@ form="form" save="post"> -
        - - - - - - - - - -
        {{::code}}
        -
        {{::name}}
        -
        -
        - - -
        {{::id}}
        -
        {{::description}}
        -
        -
        -
        - - - - -
        - - - - - - - -
        +
        + + + + + + + + +
        {{::code}}
        +
        {{::name}}
        +
        +
        + + +
        {{::id}}
        +
        {{::description}}
        +
        +
        +
        + + + + +
        + + + + + + +
        diff --git a/modules/item/front/diary/index.html b/modules/item/front/diary/index.html index dd8c9ff9d..720a42fb7 100644 --- a/modules/item/front/diary/index.html +++ b/modules/item/front/diary/index.html @@ -6,7 +6,7 @@ auto-load="false"> - + - + show-fields="$ctrl.showFields" + vn-uvc="itemIndex"> + + + + Id + Grouping + Packing + Description + Stems + Size + Niche + Type + Category + Intrastat + Origin + Buyer + Density + Active + + + + + + + + + + + {{::item.id | zeroFill:6}} + + + {{::item.grouping | dashIfEmpty}} + {{::item.packing | dashIfEmpty}} + + + + + {{::item.stems}} + {{::item.size}} + {{::item.niche}} + + {{::item.type}} + + + {{::item.category}} + + + {{::item.intrastat}} + + {{::item.origin}} + + + {{::item.userNickname}} + + + {{::item.density}} + + + + + + + + + + + + + + + + + diff --git a/modules/item/front/index/index.js b/modules/item/front/index/index.js index 93a83c6be..087021577 100644 --- a/modules/item/front/index/index.js +++ b/modules/item/front/index/index.js @@ -14,22 +14,6 @@ class Controller { id: false, actions: false }; - this.moreOptions = [ - {callback: this.goToTicketRequest, name: 'Buy requests', acl: 'buyer'} - ]; - } - - onMoreOpen() { - let options = this.moreOptions.filter(o => this.aclService.hasAny([o.acl])); - this.$.moreButton.data = options; - } - - onMoreChange(callback) { - callback.call(this); - } - - goToTicketRequest() { - this.$state.go('item.request'); } stopEvent(event) { diff --git a/modules/item/front/main/index.html b/modules/item/front/main/index.html new file mode 100644 index 000000000..758bfd55e --- /dev/null +++ b/modules/item/front/main/index.html @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/item/front/main/index.js b/modules/item/front/main/index.js new file mode 100644 index 000000000..f34369982 --- /dev/null +++ b/modules/item/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Items extends ModuleMain {} + +ngModule.vnComponent('vnItems', { + controller: Items, + template: require('./index.html') +}); diff --git a/modules/item/front/niche/index.html b/modules/item/front/niche/index.html index b361ed1e2..70dabe671 100644 --- a/modules/item/front/niche/index.html +++ b/modules/item/front/niche/index.html @@ -11,7 +11,7 @@ data="niches" form="form"> -
        + - -
        - - - - - - - - - - Ticket ID - Shipped - Warehouse - SalesPerson - Description - Requested - Price - Atender - Item - Achieved - Concept - State - - - - - - - {{request.ticketFk}} - - - - - {{::request.shipped | date: 'dd/MM/yyyy'}} - - - {{::request.warehouse}} - - - {{::request.salesPersonNickname}} - - - {{::request.description}} - {{::request.quantity}} - {{::request.price | currency: 'EUR':2}} - - - {{::request.atenderNickname}} - - - - {{request.itemFk}} - - - - - - - {{request.saleQuantity}} - - - - - - - - {{request.itemDescription}} - - - {{$ctrl.getState(request.isOk)}} - - - - - - - - - - - -
        - + + + + + + + + + + Ticket ID + Shipped + Warehouse + SalesPerson + Description + Requested + Price + Atender + Item + Achieved + Concept + State + + + + + + + {{request.ticketFk}} + + + + + {{::request.shipped | date: 'dd/MM/yyyy'}} + + + {{::request.warehouse}} + + + {{::request.salesPersonNickname}} + + + {{::request.description}} + {{::request.quantity}} + {{::request.price | currency: 'EUR':2}} + + + {{::request.atenderNickname}} + + + + {{request.itemFk}} + + + + + + + {{request.saleQuantity}} + + + + + + + + {{request.itemDescription}} + + + {{$ctrl.getState(request.isOk)}} + + + + + + + + + + + diff --git a/modules/item/front/routes.json b/modules/item/front/routes.json index e50a7f112..bda4f18a9 100644 --- a/modules/item/front/routes.json +++ b/modules/item/front/routes.json @@ -4,17 +4,23 @@ "icon": "icon-item", "validations" : true, "dependencies": ["worker", "client", "ticket"], - "menu": [ - {"state": "item.card.basicData", "icon": "settings"}, - {"state": "item.card.tags", "icon": "icon-tags"}, - {"state": "item.card.tax", "icon": "icon-tax"}, - {"state": "item.card.niche", "icon": "place"}, - {"state": "item.card.botanical", "icon": "local_florist"}, - {"state": "item.card.itemBarcode", "icon": "icon-barcode"}, - {"state": "item.card.diary", "icon": "icon-transaction"}, - {"state": "item.card.last-entries", "icon": "icon-regentry"}, - {"state": "item.card.log", "icon": "history"} - ], + "menus": { + "main": [ + {"state": "item.index", "icon": "icon-item"}, + {"state": "item.request", "icon": "pan_tool"} + ], + "card": [ + {"state": "item.card.basicData", "icon": "settings"}, + {"state": "item.card.tags", "icon": "icon-tags"}, + {"state": "item.card.tax", "icon": "icon-tax"}, + {"state": "item.card.niche", "icon": "place"}, + {"state": "item.card.botanical", "icon": "local_florist"}, + {"state": "item.card.itemBarcode", "icon": "icon-barcode"}, + {"state": "item.card.diary", "icon": "icon-transaction"}, + {"state": "item.card.last-entries", "icon": "icon-regentry"}, + {"state": "item.card.log", "icon": "history"} + ] + }, "keybindings": [ {"key": "a", "state": "item.index"} ], @@ -24,7 +30,7 @@ "state": "item", "abstract": true, "description": "Items", - "component": "ui-view" + "component": "vn-items" }, { "url": "/index?q", "state": "item.index", diff --git a/modules/item/front/tags/index.html b/modules/item/front/tags/index.html index 7958f9e1e..b7b61107d 100644 --- a/modules/item/front/tags/index.html +++ b/modules/item/front/tags/index.html @@ -19,7 +19,7 @@ data="tags" auto-load="true"> -
        + - + { +describe('order catalogFilter()', () => { it('should return an array of items', async() => { let filter = { where: { diff --git a/modules/order/back/methods/order/specs/getItemTypeAvailable.spec.js b/modules/order/back/methods/order/specs/getItemTypeAvailable.spec.js index ac425c228..66be6462a 100644 --- a/modules/order/back/methods/order/specs/getItemTypeAvailable.spec.js +++ b/modules/order/back/methods/order/specs/getItemTypeAvailable.spec.js @@ -1,7 +1,6 @@ const app = require('vn-loopback/server/server'); -// Petición #1848 -xdescribe('order getItemTypeAvailable()', () => { +describe('order getItemTypeAvailable()', () => { it('should call the getItemTypeAvailable method with a valid order and item category', async() => { let orderId = 11; let itemCategoryId = 1; diff --git a/modules/order/back/methods/order/specs/updateBasicData.spec.js b/modules/order/back/methods/order/specs/updateBasicData.spec.js index 9b92e69ca..5e83d69a0 100644 --- a/modules/order/back/methods/order/specs/updateBasicData.spec.js +++ b/modules/order/back/methods/order/specs/updateBasicData.spec.js @@ -5,7 +5,7 @@ describe('Order updateBasicData', () => { afterAll(async done => { let validparams = {note: null}; - await app.models.Order.updateBasicData(validparams, orderId); + await app.models.Order.updateBasicData(orderId, validparams); done(); }); @@ -16,7 +16,7 @@ describe('Order updateBasicData', () => { let params = []; let orderConfirmed = 1; - await app.models.Order.updateBasicData(params, orderConfirmed) + await app.models.Order.updateBasicData(orderConfirmed, params) .catch(e => { error = e; }); @@ -30,7 +30,7 @@ describe('Order updateBasicData', () => { let params = []; let orderWithRows = 16; - await app.models.Order.updateBasicData(params, orderWithRows) + await app.models.Order.updateBasicData(orderWithRows, params) .catch(e => { error = e; }); @@ -38,17 +38,15 @@ describe('Order updateBasicData', () => { expect(error.toString()).toContain(`You can't make changes on the basic data of an confirmed order or with rows`); }); - it('should return an error if the current user is administrative and the isTaxDataChecked value is true BUT the params aint valid', async() => { - let error; + it('should skip invalid params', async() => { + let success; let invalidparams = {invalid: 'param for update'}; - await app.models.Order.updateBasicData(invalidparams, orderId) - .catch(e => { - error = e; - }); + await app.models.Order.updateBasicData(orderId, invalidparams) + .then(() => success = true); - expect(error.toString()).toContain(`You don't have enough privileges to do that`); + expect(success).toBeTruthy(); }); it('should update the client fiscal data and return the count if changes made', async() => { @@ -58,7 +56,7 @@ describe('Order updateBasicData', () => { expect(order.note).toEqual(null); - let result = await app.models.Order.updateBasicData(validparams, orderId); + let result = await app.models.Order.updateBasicData(orderId, validparams); expect(result.note).toEqual('test note'); }); diff --git a/modules/order/back/methods/order/updateBasicData.js b/modules/order/back/methods/order/updateBasicData.js index 6a8148a21..8f4393c04 100644 --- a/modules/order/back/methods/order/updateBasicData.js +++ b/modules/order/back/methods/order/updateBasicData.js @@ -1,22 +1,25 @@ let UserError = require('vn-loopback/util/user-error'); +let pick = require('object.pick'); module.exports = Self => { Self.remoteMethod('updateBasicData', { description: 'Updates basic data of an order', accessType: 'WRITE', - accepts: [{ - arg: 'data', - type: 'Object', - required: true, - description: 'Params to update', - http: {source: 'body'} - }, { - arg: 'id', - type: 'string', - required: true, - description: 'Model id', - http: {source: 'path'} - }], + accepts: [ + { + arg: 'id', + type: 'string', + required: true, + description: 'Model id', + http: {source: 'path'} + }, { + arg: 'data', + type: 'Object', + required: true, + description: 'Params to update', + http: {source: 'body'} + } + ], returns: { arg: 'order', type: 'Object', @@ -28,25 +31,25 @@ module.exports = Self => { } }); - Self.updateBasicData = async(params, id) => { - let order = await Self.app.models.Order.findById(id); - let orderRows = await Self.app.models.OrderRow.find({where: {orderFk: id}}); + Self.updateBasicData = async(id, params) => { + let models = Self.app.models; + + let order = await models.Order.findById(id); + let orderRows = await models.OrderRow.find({where: {orderFk: id}}); if (order.isConfirmed || orderRows.length != 0) throw new UserError(`You can't make changes on the basic data of an confirmed order or with rows`); - let validUpdateParams = [ + let updateParams = pick(params, [ 'clientFk', 'addressFk', 'landed', 'agencyModeFk', 'note', - ]; + ]); + if (Object.keys(updateParams).length) + await order.updateAttributes(updateParams); - for (const key in params) { - if (validUpdateParams.indexOf(key) === -1) - throw new UserError(`You don't have enough privileges to do that`); - } - return await order.updateAttributes(params); + return await order; }; }; diff --git a/modules/order/front/basic-data/index.html b/modules/order/front/basic-data/index.html index d79398299..eaa73a25c 100644 --- a/modules/order/front/basic-data/index.html +++ b/modules/order/front/basic-data/index.html @@ -12,7 +12,7 @@ form="form" save="patch"> - + { + $scope.$watch('$ctrl.selection', newValue => { if (newValue) { $scope.addressModel.where = {clientFk: newValue.id}; $scope.addressModel.refresh(); diff --git a/modules/order/front/card/index.html b/modules/order/front/card/index.html index 96aa94f6c..4f10c1728 100644 --- a/modules/order/front/card/index.html +++ b/modules/order/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/order/front/card/index.js b/modules/order/front/card/index.js index 9524a8ec4..e79167761 100644 --- a/modules/order/front/card/index.js +++ b/modules/order/front/card/index.js @@ -1,31 +1,35 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $state) { - this.$http = $http; - this.$state = $state; - this.order = {}; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { include: [ { - relation: 'agencyMode', scope: { + relation: 'agencyMode', + scope: { fields: ['name'] } - }, - { - relation: 'address', scope: { + }, { + relation: 'address', + scope: { fields: ['nickname'] } - }, - { - relation: 'rows', scope: { + }, { + relation: 'rows', + scope: { fields: ['id'] } - }, - { + }, { relation: 'client', scope: { - fields: ['salesPersonFk', 'name', 'isActive', 'isFreezed', 'isTaxDataChecked'], + fields: [ + 'salesPersonFk', + 'name', + 'isActive', + 'isFreezed', + 'isTaxDataChecked' + ], include: { relation: 'salesPerson', scope: { @@ -42,40 +46,18 @@ class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - let json = encodeURIComponent(JSON.stringify(this.filter)); - let query = `Orders/${this.$state.params.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data && res.data.rows) { - if (res.data.rows.length == 0) - delete res.data.rows; - this.order = res.data; - this.getTotal(); - } - }); - } - - reload() { - this.getCard(); - } - - getTotal() { - let query = `Orders/${this.$state.params.id}/getTotal`; - this.$http.get(query).then(res => { - if (res.data) - this.order.total = res.data; + this.$q.all([ + this.$http.get(`Orders/${this.$params.id}`, {filter}) + .then(res => this.order = res.data), + this.$http.get(`Orders/${this.$params.id}/getTotal`) + .then(res => ({total: res.data})) + ]).then(res => { + this.order = Object.assign.apply(null, res); }); } } -Controller.$inject = ['$http', '$state']; - ngModule.component('vnOrderCard', { template: require('./index.html'), controller: Controller diff --git a/modules/order/front/card/index.spec.js b/modules/order/front/card/index.spec.js index a962ba5ec..19d070920 100644 --- a/modules/order/front/card/index.spec.js +++ b/modules/order/front/card/index.spec.js @@ -2,61 +2,29 @@ import './index.js'; describe('Order', () => { describe('Component vnOrderCard', () => { - let $scope; let controller; let $httpBackend; - let $state; + let data = {id: 1, name: 'fooName'}; + let total = 10.5; beforeEach(ngModule('order')); - beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_) => { + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $scope.card = {createOrder: () => {}}; - $state = {params: {id: 1}}; - controller = $componentController('vnOrderCard', {$scope, $state}); + + let $element = angular.element('
        '); + controller = $componentController('vnOrderCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Orders/:id').respond(data); + $httpBackend.whenRoute('GET', 'Orders/:id/getTotal').respond(200, total); })); - describe('getCard()', () => { - it(`should make a query, save the data in order and call get order if the response has data`, () => { - spyOn(controller, 'getTotal'); - let json = encodeURIComponent(JSON.stringify(controller.filter)); - $httpBackend.expectGET(`Orders/${controller.$state.params.id}?filter=${json}`).respond({rows: [1, 2, 3]}); - controller.getCard(); - $httpBackend.flush(); + it('should request data and total, merge them, and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - expect(controller.order).toEqual({rows: [1, 2, 3]}); - expect(controller.getTotal).toHaveBeenCalledWith(); - }); - - it(`should make a query and not call getTotal if the response is not defined`, () => { - spyOn(controller, 'getTotal'); - let json = encodeURIComponent(JSON.stringify(controller.filter)); - $httpBackend.expectGET(`Orders/${controller.$state.params.id}?filter=${json}`).respond(undefined); - controller.getCard(); - $httpBackend.flush(); - - expect(controller.order).toEqual({}); - expect(controller.getTotal).not.toHaveBeenCalledWith(); - }); - }); - - describe('getTotal()', () => { - it(`should make a query and save the data in order.total`, () => { - $httpBackend.expectGET(`Orders/${controller.$state.params.id}/getTotal`).respond('20M'); - controller.getTotal(); - $httpBackend.flush(); - - expect(controller.order.total).toEqual('20M'); - }); - - it(`should make a query and not save the respones if is not defined`, () => { - $httpBackend.expectGET(`Orders/${controller.$state.params.id}/getTotal`).respond(); - controller.getTotal(); - $httpBackend.flush(); - - expect(controller.order.total).toEqual(undefined); - }); + expect(controller.order).toEqual(Object.assign({}, data, {total})); }); }); }); diff --git a/modules/order/front/catalog/index.html b/modules/order/front/catalog/index.html index 37ef1fd90..8002c2b10 100644 --- a/modules/order/front/catalog/index.html +++ b/modules/order/front/catalog/index.html @@ -7,8 +7,7 @@ on-data-change="$ctrl.onDataChange()"> + model="model">
        diff --git a/modules/order/front/create/card.js b/modules/order/front/create/card.js index 74d1d79c8..7f1bb6133 100644 --- a/modules/order/front/create/card.js +++ b/modules/order/front/create/card.js @@ -78,16 +78,16 @@ class Controller { } getAvailableAgencies() { - this.order.agencyModeFk = null; - if (this.order.landed && this.order.addressFk) { - let filter = { - addressFk: this.order.addressFk, - landed: this.order.landed - }; - this.$http.get(`Agencies/landsThatDay`, {params: {filter}}) - .then(res => { - this._availableAgencies = res.data; - }); + let order = this.order; + order.agencyModeFk = null; + + let params = { + addressFk: order.addressFk, + landed: order.landed + }; + if (params.landed && params.addressFk) { + this.$http.get(`Agencies/landsThatDay`, {params}) + .then(res => this._availableAgencies = res.data); } } diff --git a/modules/order/front/create/index.html b/modules/order/front/create/index.html index d557bd21a..11de7ff6c 100644 --- a/modules/order/front/create/index.html +++ b/modules/order/front/create/index.html @@ -1,11 +1,9 @@ -
        -
        - - - - - - - -
        +
        + + + + + + +
        \ No newline at end of file diff --git a/modules/order/front/descriptor/index.js b/modules/order/front/descriptor/index.js index ec65d3b4c..0d8a1e14a 100644 --- a/modules/order/front/descriptor/index.js +++ b/modules/order/front/descriptor/index.js @@ -14,6 +14,7 @@ class Controller { set order(value) { this._order = value; + if (!value) return; if (value.isConfirmed) { this._quicklinks = { diff --git a/modules/order/front/filter/index.js b/modules/order/front/filter/index.js index 71e217087..03dd0fa33 100644 --- a/modules/order/front/filter/index.js +++ b/modules/order/front/filter/index.js @@ -24,8 +24,7 @@ class Controller { * @param {Object} value - Order data */ set order(value) { - if (!value.id || this._order) return; - + if (!value || !value.id || this._order) return; this._order = value; this.$.$applyAsync(() => { diff --git a/modules/order/front/index.js b/modules/order/front/index.js index 2232a7501..0d8d0d686 100644 --- a/modules/order/front/index.js +++ b/modules/order/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './card'; import './descriptor'; diff --git a/modules/order/front/index/index.html b/modules/order/front/index/index.html index a6ec84392..20dd4a231 100644 --- a/modules/order/front/index/index.html +++ b/modules/order/front/index/index.html @@ -5,74 +5,71 @@ data="orders" order="landed DESC, clientFk"> -
        - - - + + + + + + + + Id + Client + Sales person + Confirmed + Created from + Created + Landed + Company + Total + + + + + {{::order.id}} + + + {{::order.clientName}} + + + + + {{::order.workerNickname | dashIfEmpty}} + + + + + + + {{::order.sourceApp}} + {{::order.created | date: 'dd/MM/yyyy HH:mm'}} + {{::order.landed | date:'dd/MM/yyyy'}} + {{::order.companyCode}} + {{::order.total | currency: 'EUR': 2 | dashIfEmpty}} + + + + + + + - - - - - - Id - Client - Sales person - Confirmed - Created from - Created - Landed - Company - Total - - - - - {{::order.id}} - - - {{::order.clientName}} - - - - - {{::order.workerNickname | dashIfEmpty}} - - - - - - - {{::order.sourceApp}} - {{::order.created | date: 'dd/MM/yyyy HH:mm'}} - {{::order.landed | date:'dd/MM/yyyy'}} - {{::order.companyCode}} - {{::order.total | currency: 'EUR': 2 | dashIfEmpty}} - - - - - - - - - -
        - + + -
        @@ -28,7 +24,8 @@ Shipped Quantity Price - + Amount + @@ -59,11 +56,14 @@ {{::row.price | currency: 'EUR':2}} - + + {{::row.price * row.quantity | currency: 'EUR':2}} + + @@ -84,7 +84,7 @@ \ No newline at end of file diff --git a/modules/order/front/line/index.js b/modules/order/front/line/index.js index 14e29c5c5..149251c87 100644 --- a/modules/order/front/line/index.js +++ b/modules/order/front/line/index.js @@ -1,16 +1,8 @@ import ngModule from '../module'; +import Section from 'salix/components/section'; import './style.scss'; -class Controller { - constructor($scope, $state, $http, vnApp, $translate) { - this.$scope = $scope; - this.vnApp = vnApp; - this.$translate = $translate; - this.$state = $state; - this.$http = $http; - this.idsToRemove = []; - } - +class Controller extends Section { $onInit() { this.getRows(); } @@ -24,82 +16,56 @@ class Controller { return this._order; } + get subtotal() { + return this.order ? this.order.total - this.VAT : 0; + } + getRows() { let filter = { - where: {orderFk: this.$state.params.id}, - include: [{ - relation: 'item' - }, - {relation: 'warehouse'}] + where: {orderFk: this.$params.id}, + include: [ + {relation: 'item'}, + {relation: 'warehouse'} + ] }; - filter = encodeURIComponent(JSON.stringify(filter)); - let query = `OrderRows?filter=${filter}`; - - this.$http.get(query).then(res => { - this.rows = res.data; - }); + this.$http.get(`OrderRows`, {filter}) + .then(res => this.rows = res.data); } getVAT() { - let query = `Orders/${this.$state.params.id}/getVAT`; + this.$http.get(`Orders/${this.$params.id}/getVAT`) + .then(res => this.VAT = res.data); + } - this.$http.get(query).then(res => { - this.VAT = res.data; + deleteRow(index) { + let [row] = this.rows.splice(index, 1); + let params = { + rows: [row.id], + actualOrderId: this.$params.id + }; + return this.$http.post(`OrderRows/removes`, params).then(() => { + this.card.reload(); + this.vnApp.showSuccess(this.$t('Data saved!')); }); } - get subtotal() { - return this.order.total - this.VAT || 0; - } - - showDeleteRow(index) { - this.lineIdToRemove = index; - this.$scope.deleteRow.show(); - } - - deleteRow(response) { - if (response == 'accept') { - let [lineRemoved] = this.rows.splice(this.lineIdToRemove, 1); - this.idsToRemove.push(lineRemoved.id); - let params = { - rows: this.idsToRemove, - actualOrderId: this.$state.params.id - }; - let query = `OrderRows/removes`; - - this.$http.post(query, params).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Data saved!')); - }); - this.$scope.watcher.updateOriginalData(); - this.getVAT(); - this.card.reload(); - } - this.lineIdToRemove = undefined; - } - - // Item Descriptor showDescriptor(event, itemFk) { - this.$scope.descriptor.itemFk = itemFk; - this.$scope.descriptor.parent = event.target; - this.$scope.descriptor.show(); - } - - onDescriptorLoad() { - this.$scope.popover.relocate(); + let descriptor = this.$.descriptor; + descriptor.itemFk = itemFk; + descriptor.parent = event.target; + descriptor.show(); } save() { - let query = `Orders/${this.order.id}/confirm`; - - this.$http.post(query).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Order confirmed')); - this.$state.go(`ticket.index`, {q: JSON.stringify({clientFk: this.order.clientFk})}); + this.$http.post(`Orders/${this.$params.id}/confirm`).then(() => { + this.vnApp.showSuccess(this.$t('Order confirmed')); + this.$state.go(`ticket.index`, { + q: JSON.stringify({clientFk: this.order.clientFk}) + }); }); } } -Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate']; - ngModule.component('vnOrderLine', { template: require('./index.html'), controller: Controller, diff --git a/modules/order/front/line/index.spec.js b/modules/order/front/line/index.spec.js index c3a01bbe0..543ffeedc 100644 --- a/modules/order/front/line/index.spec.js +++ b/modules/order/front/line/index.spec.js @@ -1,5 +1,4 @@ import './index.js'; -import watcher from 'core/mocks/watcher'; describe('Order', () => { describe('Component vnOrderLine', () => { @@ -7,83 +6,60 @@ describe('Order', () => { let controller; let $httpBackend; + let rows = [ + { + quantity: 4, + price: 10.5 + }, { + quantity: 3, + price: 2.4 + } + ]; + beforeEach(ngModule('order')); beforeEach(angular.mock.inject(($componentController, _$state_, _$httpBackend_) => { $state = _$state_; - $state = {params: {id: 1}}; $httpBackend = _$httpBackend_; - controller = $componentController('vnOrderLine', {$state}); - controller.rows = [{id: 1}]; - controller.$scope.watcher = watcher; - controller.$scope.popover = {relocate: () => {}}; - controller.$scope.descriptor = {show: () => {}}; - controller.vnApp = {showSuccess: () => {}}; - controller.card = {reload: () => {}}; + + $state.params.id = 1; + $httpBackend.whenRoute('GET', `OrderRows`).respond(rows); + $httpBackend.whenRoute('GET', `Orders/:id/getVAT`).respond(200, 10.5); + + let $element = angular.element('
        '); + controller = $componentController('vnOrderLine', {$element}); })); describe('getRows()', () => { it('should make a query to get the rows of a given order', () => { - let filter = { - where: {orderFk: controller.$state.params.id}, - include: [{ - relation: 'item' - }, - {relation: 'warehouse'}] - }; - - filter = encodeURIComponent(JSON.stringify(filter)); - $httpBackend.expectGET(`OrderRows?filter=${filter}`).respond({data: [{id: 1}]}); controller.getRows(); $httpBackend.flush(); + + expect(controller.rows).toBeDefined(); }); }); describe('getVAT()', () => { it('should make a query to get the VAT of a given order', () => { - $httpBackend.expectGET(`Orders/1/getVAT`).respond({data: {tax: 3}}); controller.getVAT(); $httpBackend.flush(); + + expect(controller.VAT).toBeDefined(); }); }); describe('deleteRow()', () => { it('should remove a row from rows and add save the data if the response is accept', () => { - expect(controller.rows.length).toBe(1); - spyOn(controller.vnApp, 'showSuccess'); - spyOn(controller, 'getVAT'); - spyOn(controller.card, 'reload'); - controller.deleteRow('accept'); - - $httpBackend.expectPOST(`OrderRows/removes`).respond(); + controller.getRows(); $httpBackend.flush(); - expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + controller.card = {reload: jasmine.createSpy('reload')}; + $httpBackend.expectPOST(`OrderRows/removes`).respond(); + controller.deleteRow(0); + $httpBackend.flush(); + + expect(controller.rows.length).toBe(1); expect(controller.card.reload).toHaveBeenCalledWith(); - expect(controller.rows.length).toBe(0); - expect(controller.lineIdToRemove).toBe(undefined); - }); - }); - - describe('showDescriptor()', () => { - it('should set $scope.descriptor.itemFk, $scope.descriptor.parent and call $scope.descriptor.show()', () => { - let event = {target: 1}; - let itemFk = 1; - spyOn(controller.$scope.descriptor, 'show'); - controller.showDescriptor(event, itemFk); - - expect(controller.$scope.descriptor.itemFk).toBe(1); - expect(controller.$scope.descriptor.parent).toBe(1); - expect(controller.$scope.descriptor.show).toHaveBeenCalledWith(); - }); - }); - - describe('onDescriptorLoad()', () => { - it('should call $scope.popover.relocate()', () => { - spyOn(controller.$scope.popover, 'relocate'); - controller.onDescriptorLoad(); - - expect(controller.$scope.popover.relocate).toHaveBeenCalledWith(); }); }); }); diff --git a/modules/order/front/main/index.html b/modules/order/front/main/index.html new file mode 100644 index 000000000..09d498599 --- /dev/null +++ b/modules/order/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/order/front/main/index.js b/modules/order/front/main/index.js new file mode 100644 index 000000000..7513d2144 --- /dev/null +++ b/modules/order/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Order extends ModuleMain {} + +ngModule.vnComponent('vnOrder', { + controller: Order, + template: require('./index.html') +}); diff --git a/modules/order/front/routes.json b/modules/order/front/routes.json index 0f610169d..86eda488f 100644 --- a/modules/order/front/routes.json +++ b/modules/order/front/routes.json @@ -4,18 +4,23 @@ "icon": "icon-basket", "validations": true, "dependencies": ["worker", "item", "ticket"], - "menu": [ - {"state": "order.card.basicData", "icon": "settings"}, - {"state": "order.card.catalog", "icon": "icon-basket"}, - {"state": "order.card.volume", "icon": "icon-volume"}, - {"state": "order.card.line", "icon": "icon-lines"} - ], + "menus": { + "main": [ + {"state": "order.index", "icon": "icon-basket"} + ], + "card": [ + {"state": "order.card.basicData", "icon": "settings"}, + {"state": "order.card.catalog", "icon": "icon-basket"}, + {"state": "order.card.volume", "icon": "icon-volume"}, + {"state": "order.card.line", "icon": "icon-lines"} + ] + }, "routes": [ { "url": "/order", "state": "order", "abstract": true, - "component": "ui-view", + "component": "vn-order", "description": "Orders" }, { "url": "/index?q", diff --git a/modules/route/front/basic-data/index.html b/modules/route/front/basic-data/index.html index b63970bcc..dcdfd37d7 100644 --- a/modules/route/front/basic-data/index.html +++ b/modules/route/front/basic-data/index.html @@ -5,7 +5,7 @@ form="form" save="patch"> - + - - - - -
        - + + + + + diff --git a/modules/route/front/card/index.js b/modules/route/front/card/index.js index 76471002a..0760259d3 100644 --- a/modules/route/front/card/index.js +++ b/modules/route/front/card/index.js @@ -1,11 +1,9 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($stateParams, $http) { - this.$http = $http; - this.$stateParams = $stateParams; - this.route = null; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { fields: [ 'id', 'workerFk', @@ -22,22 +20,18 @@ export default class Controller { 'cost', 'zoneFk' ], - - where: {id: $stateParams.id}, include: [ { relation: 'agencyMode', scope: { fields: ['id', 'name'] } - }, - { + }, { relation: 'vehicle', scope: { fields: ['id', 'm3'] } - }, - { + }, { relation: 'zone', scope: { fields: ['id', 'name'] @@ -63,24 +57,11 @@ export default class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - let json = encodeURIComponent(JSON.stringify(this.filter)); - this.$http.get(`Routes?filter=${json}`).then(response => { - this.route = response.data[0]; - }); - } - - reload() { - this.getCard(); + this.$http.get(`Routes/${this.$params.id}`, {filter}) + .then(res => this.route = res.data); } } -Controller.$inject = ['$stateParams', '$http']; ngModule.component('vnRouteCard', { template: require('./index.html'), diff --git a/modules/route/front/create/index.html b/modules/route/front/create/index.html index 97747fca5..7f6a1f600 100644 --- a/modules/route/front/create/index.html +++ b/modules/route/front/create/index.html @@ -5,56 +5,54 @@ form="form" save="post"> -
        - - - - - - - - - - - - - - - - - - - - - - - - -
        +
        + + + + + + + + + + + + + + + + + + + + + + +
        diff --git a/modules/route/front/descriptor/index.js b/modules/route/front/descriptor/index.js index 402b9102f..cfa718855 100644 --- a/modules/route/front/descriptor/index.js +++ b/modules/route/front/descriptor/index.js @@ -70,7 +70,7 @@ class Controller { let url = `Routes/${this.route.id}/updateVolume`; this.$http.post(url).then(() => { this.vnApp.showSuccess(this.$translate.instant('Volume updated')); - this.card.reload(); + if (this.card) this.card.reload(); }); } } @@ -85,7 +85,7 @@ ngModule.component('vnRouteDescriptor', { quicklinks: '<' }, require: { - card: '^vnRouteCard' + card: '^?vnRouteCard' }, controller: Controller }); diff --git a/modules/route/front/index.js b/modules/route/front/index.js index a6530c6b8..ce8e80e95 100644 --- a/modules/route/front/index.js +++ b/modules/route/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './descriptor'; diff --git a/modules/route/front/index/index.html b/modules/route/front/index/index.html index 9852eb880..c01112928 100644 --- a/modules/route/front/index/index.html +++ b/modules/route/front/index/index.html @@ -6,63 +6,55 @@ order="created DESC" auto-load="true"> - + diff --git a/modules/route/front/index/index.js b/modules/route/front/index/index.js index 823ef8bf4..8fdde810c 100644 --- a/modules/route/front/index/index.js +++ b/modules/route/front/index/index.js @@ -1,30 +1,15 @@ import ngModule from '../module'; export default class Controller { - constructor($scope, vnToken, vnConfig) { - this.accessToken = vnToken.token; + constructor($scope, vnToken) { this.$ = $scope; - this.vnConfig = vnConfig; - - this.setDefaultFilter(); - } - - setDefaultFilter() { - let to = new Date(); - to.setDate(to.getDate() + 1); - to.setHours(0, 0, 0, 0); - - let from = new Date(); - from.setHours(0, 0, 0, 0); - - this.filter = {from, to, warehouseFk: this.vnConfig.warehouseFk}; + this.accessToken = vnToken.token; } showWorkerDescriptor(event, workerFk) { if (event.defaultPrevented) return; - event.preventDefault(); - event.stopImmediatePropagation(); + event.stopPropagation(); this.selectedWorker = workerFk; this.$.workerDescriptor.parent = event.target; @@ -32,10 +17,12 @@ export default class Controller { } preview(event, route) { + if (event.defaultPrevented) return; + event.preventDefault(); + event.stopPropagation(); + this.routeSelected = route; this.$.summary.show(); - event.preventDefault(); - event.stopImmediatePropagation(); } onSearch(params) { @@ -45,8 +32,7 @@ export default class Controller { this.$.model.clear(); } } - -Controller.$inject = ['$scope', 'vnToken', 'vnConfig']; +Controller.$inject = ['$scope', 'vnToken']; ngModule.component('vnRouteIndex', { template: require('./index.html'), diff --git a/modules/route/front/index/style.scss b/modules/route/front/index/style.scss deleted file mode 100644 index d8c24a482..000000000 --- a/modules/route/front/index/style.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import "./variables"; - -vn-route-index .index { - max-width: $width-lg; -} \ No newline at end of file diff --git a/modules/route/front/main/index.html b/modules/route/front/main/index.html new file mode 100644 index 000000000..f6e097b24 --- /dev/null +++ b/modules/route/front/main/index.html @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/route/front/main/index.js b/modules/route/front/main/index.js new file mode 100644 index 000000000..618989748 --- /dev/null +++ b/modules/route/front/main/index.js @@ -0,0 +1,26 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Route extends ModuleMain { + constructor($element, $, vnConfig) { + super($element, $); + this.vnConfig = vnConfig; + } + + $postLink() { + let to = new Date(); + to.setDate(to.getDate() + 1); + to.setHours(0, 0, 0, 0); + + let from = new Date(); + from.setHours(0, 0, 0, 0); + + this.filter = {from, to, warehouseFk: this.vnConfig.warehouseFk}; + } +} +Route.$inject = ['$element', '$scope', 'vnConfig']; + +ngModule.vnComponent('vnRoute', { + controller: Route, + template: require('./index.html') +}); diff --git a/modules/route/front/routes.json b/modules/route/front/routes.json index baffe9082..beb444abc 100644 --- a/modules/route/front/routes.json +++ b/modules/route/front/routes.json @@ -4,16 +4,22 @@ "icon": "icon-delivery", "validations" : true, "dependencies": ["client", "worker", "ticket"], - "menu": [ - {"state": "route.card.basicData", "icon": "settings"}, - {"state": "route.card.tickets", "icon": "icon-ticket"}, - {"state": "route.card.log", "icon": "history"}], + "menus": { + "main": [ + {"state": "route.index", "icon": "icon-delivery"} + ], + "card": [ + {"state": "route.card.basicData", "icon": "settings"}, + {"state": "route.card.tickets", "icon": "icon-ticket"}, + {"state": "route.card.log", "icon": "history"} + ] + }, "routes": [ { "url": "/route", "state": "route", "abstract": true, - "component": "ui-view", + "component": "vn-route", "description": "Routes", "acl": ["employee"] }, { diff --git a/modules/ticket/back/methods/sale/calculate.js b/modules/ticket/back/methods/sale/calculate.js new file mode 100644 index 000000000..5a2ac39b8 --- /dev/null +++ b/modules/ticket/back/methods/sale/calculate.js @@ -0,0 +1,34 @@ +const UserError = require('vn-loopback/util/user-error'); +module.exports = Self => { + Self.remoteMethodCtx('calculate', { + description: 'Calculates the price of a sale and its components', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + description: 'The sale id', + type: 'number', + required: true, + http: {source: 'path'} + }], + returns: { + type: 'Number', + root: true + }, + http: { + path: `/:id/calculate`, + verb: 'post' + } + }); + + Self.calculate = async(ctx, id) => { + const models = Self.app.models; + + const sale = await Self.findById(id); + const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); + + if (!isEditable) + throw new UserError(`The sales of this ticket can't be modified`); + + return Self.rawSql('CALL vn.ticketCalculateSale(?)', [id]); + }; +}; diff --git a/modules/ticket/back/methods/sale/specs/calculate.spec.js b/modules/ticket/back/methods/sale/specs/calculate.spec.js new file mode 100644 index 000000000..48b14e972 --- /dev/null +++ b/modules/ticket/back/methods/sale/specs/calculate.spec.js @@ -0,0 +1,24 @@ +const app = require('vn-loopback/server/server'); + +describe('sale calculate()', () => { + const saleId = 7; + + it('should update the sale price', async() => { + const ctx = {req: {accessToken: {userId: 9}}}; + const response = await app.models.Sale.calculate(ctx, saleId); + + expect(response.affectedRows).toBeDefined(); + }); + + it('should throw an error if the ticket is not editable', async() => { + const ctx = {req: {accessToken: {userId: 9}}}; + const immutableSaleId = 1; + await app.models.Sale.calculate(ctx, immutableSaleId) + .catch(response => { + expect(response).toEqual(new Error(`The sales of this ticket can't be modified`)); + error = response; + }); + + expect(error).toBeDefined(); + }); +}); diff --git a/modules/ticket/back/methods/ticket-request/filter.js b/modules/ticket/back/methods/ticket-request/filter.js index 82e1ee8e0..8f1f837d2 100644 --- a/modules/ticket/back/methods/ticket-request/filter.js +++ b/modules/ticket/back/methods/ticket-request/filter.js @@ -76,7 +76,7 @@ module.exports = Self => { case 'ticketFk': return {'t.id': value}; case 'attenderFk': - return {'tr.atenderFk': value}; + return {'tr.attenderFk': value}; case 'isOk': return {'tr.isOk': value}; case 'clientFk': @@ -106,7 +106,7 @@ module.exports = Self => { tr.ticketFk, tr.quantity, tr.price, - tr.atenderFk attenderFk, + tr.attenderFk, tr.description, tr.response, tr.saleFk, @@ -131,7 +131,7 @@ module.exports = Self => { LEFT JOIN sale s ON s.id = tr.saleFk LEFT JOIN worker wk ON wk.id = c.salesPersonFk LEFT JOIN account.user u ON u.id = wk.userFk - LEFT JOIN worker wka ON wka.id = tr.atenderFk + LEFT JOIN worker wka ON wka.id = tr.attenderFk LEFT JOIN account.user ua ON ua.id = wka.userFk`); stmt.merge(conn.makeSuffix(filter)); diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js index b9ac03560..fb72c7dbc 100644 --- a/modules/ticket/back/methods/ticket/setDeleted.js +++ b/modules/ticket/back/methods/ticket/setDeleted.js @@ -27,7 +27,7 @@ module.exports = Self => { const $t = ctx.req.__; // $translate if (!isEditable) - throw new UserError('You cannot delete this ticket because is already invoiced, deleted or prepared'); + throw new UserError(`The sales of this ticket can't be modified`); // Check if has sales with shelving const sales = await models.Sale.find({ diff --git a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js index d3473a51b..25ce90946 100644 --- a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js +++ b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js @@ -1,6 +1,6 @@ const app = require('vn-loopback/server/server'); -// Petición #1848 -xdescribe('ticket componentUpdate()', () => { + +describe('ticket componentUpdate()', () => { const ticketId = 11; const today = new Date(); const tomorrow = new Date(); diff --git a/modules/ticket/back/methods/ticket/specs/new.spec.js b/modules/ticket/back/methods/ticket/specs/new.spec.js index 12ae47bbc..80147c2f2 100644 --- a/modules/ticket/back/methods/ticket/specs/new.spec.js +++ b/modules/ticket/back/methods/ticket/specs/new.spec.js @@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server'); let UserError = require('vn-loopback/util/user-error'); -xdescribe('ticket new()', () => { +describe('ticket new()', () => { let ticket; let today = new Date(); let ctx = {req: {accessToken: {userId: 1}}}; diff --git a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js index c82e41e53..61060c9a2 100644 --- a/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js +++ b/modules/ticket/back/methods/ticket/specs/priceDifference.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); let UserError = require('vn-loopback/util/user-error'); -xdescribe('sale priceDifference()', () => { +describe('sale priceDifference()', () => { it('should return ticket price differences', async() => { let tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); diff --git a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js index 737645d85..890fc6c45 100644 --- a/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js +++ b/modules/ticket/back/methods/ticket/specs/setDeleted.spec.js @@ -1,7 +1,6 @@ const app = require('vn-loopback/server/server'); -// Excluded waiting for #1874 -xdescribe('ticket deleted()', () => { +describe('ticket deleted()', () => { let ticket; let ctx; diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js index b1fe82565..1b352bcff 100644 --- a/modules/ticket/back/models/sale.js +++ b/modules/ticket/back/models/sale.js @@ -5,6 +5,7 @@ module.exports = Self => { require('../methods/sale/updatePrice')(Self); require('../methods/sale/updateQuantity')(Self); require('../methods/sale/updateConcept')(Self); + require('../methods/sale/calculate')(Self); Self.validatesPresenceOf('concept', { message: `Concept cannot be blank` diff --git a/modules/ticket/back/models/ticket-request.json b/modules/ticket/back/models/ticket-request.json index 7f1cb4b02..f216cd531 100644 --- a/modules/ticket/back/models/ticket-request.json +++ b/modules/ticket/back/models/ticket-request.json @@ -33,13 +33,6 @@ "isOk": { "type": "Boolean" }, - "attenderFk": { - "type": "Number", - "required": true, - "mysql": { - "columnName": "atenderFk" - } - }, "response": { "type": "String" } diff --git a/modules/ticket/front/basic-data/index.html b/modules/ticket/front/basic-data/index.html index 4439069e8..f313110b3 100644 --- a/modules/ticket/front/basic-data/index.html +++ b/modules/ticket/front/basic-data/index.html @@ -9,6 +9,4 @@ on-step-end="$ctrl.onSubmit()"> -
        - -
        \ No newline at end of file + diff --git a/modules/ticket/front/basic-data/step-one/index.html b/modules/ticket/front/basic-data/step-one/index.html index b98267a85..ccdf8a15f 100644 --- a/modules/ticket/front/basic-data/step-one/index.html +++ b/modules/ticket/front/basic-data/step-one/index.html @@ -5,7 +5,7 @@ auto-load="true">
        - + { if (res.data) { this.ticket.zoneFk = res.data.zoneFk; - this.ticket.agencyModeFk = res.data.agencyModeFk; this.ticket.landed = res.data.landed; this.ticket.shipped = params.shipped; } else { @@ -217,7 +220,6 @@ class Controller { this.$http.get(query, {params}).then(res => { if (res.data) { this.ticket.zoneFk = res.data.zoneFk; - this.ticket.agencyModeFk = res.data.agencyModeFk; this.ticket.landed = params.landed; this.ticket.shipped = res.data.shipped; } else { diff --git a/modules/ticket/front/basic-data/step-one/index.spec.js b/modules/ticket/front/basic-data/step-one/index.spec.js index 77d20bda5..690399131 100644 --- a/modules/ticket/front/basic-data/step-one/index.spec.js +++ b/modules/ticket/front/basic-data/step-one/index.spec.js @@ -69,14 +69,15 @@ describe('Ticket', () => { controller.ticket.addressFk = 99; controller.addressId = 100; const landed = new Date(); - const spectedResult = { + const expectedResult = { landed: landed, addressFk: 100, - agencyModeFk: 7 + agencyModeFk: 7, + warehouseFk: 1 }; controller.landed = landed; - expect(controller.getShipped).toHaveBeenCalledWith(spectedResult); + expect(controller.getShipped).toHaveBeenCalledWith(expectedResult); expect(controller.ticket.addressFk).toEqual(100); }); }); @@ -95,14 +96,15 @@ describe('Ticket', () => { controller.ticket.warehouseId = 1; controller.warehouseId = 2; const landed = new Date(); - const spectedResult = { + const expectedResult = { landed: landed, addressFk: 121, - agencyModeFk: 7 + agencyModeFk: 7, + warehouseFk: 2 }; controller.landed = landed; - expect(controller.getShipped).toHaveBeenCalledWith(spectedResult); + expect(controller.getShipped).toHaveBeenCalledWith(expectedResult); expect(controller.ticket.warehouseFk).toEqual(2); }); }); @@ -120,14 +122,15 @@ describe('Ticket', () => { it('should set shipped property and call getLanded() method ', () => { spyOn(controller, 'getLanded'); const shipped = new Date(); - const spectedResult = { + const expectedResult = { shipped: shipped, addressFk: 121, - agencyModeFk: 7 + agencyModeFk: 7, + warehouseFk: 1 }; controller.shipped = shipped; - expect(controller.getLanded).toHaveBeenCalledWith(spectedResult); + expect(controller.getLanded).toHaveBeenCalledWith(expectedResult); }); }); @@ -144,15 +147,16 @@ describe('Ticket', () => { it('should set shipped property and call getShipped() method ', () => { spyOn(controller, 'getShipped'); const landed = new Date(); - const spectedResult = { + const expectedResult = { landed: landed, addressFk: 121, - agencyModeFk: 7 + agencyModeFk: 7, + warehouseFk: 1 }; controller.landed = landed; - expect(controller.getShipped).toHaveBeenCalledWith(spectedResult); + expect(controller.getShipped).toHaveBeenCalledWith(expectedResult); }); }); @@ -169,30 +173,32 @@ describe('Ticket', () => { spyOn(controller, 'getLanded'); const shipped = new Date(); const agencyModeId = 8; - const spectedResult = { + const expectedResult = { shipped: shipped, addressFk: 121, - agencyModeFk: agencyModeId + agencyModeFk: agencyModeId, + warehouseFk: 1 }; controller.ticket.shipped = shipped; controller.agencyModeId = 8; - expect(controller.getLanded).toHaveBeenCalledWith(spectedResult); + expect(controller.getLanded).toHaveBeenCalledWith(expectedResult); }); it('should do nothing if attempting to set the same agencyMode id', () => { spyOn(controller, 'getShipped'); const landed = new Date(); const agencyModeId = 7; - const spectedResult = { + const expectedResult = { landed: landed, addressFk: 121, - agencyModeFk: agencyModeId + agencyModeFk: agencyModeId, + warehouseFk: 1 }; controller.ticket.landed = landed; controller.agencyModeId = 7; - expect(controller.getShipped).not.toHaveBeenCalledWith(spectedResult); + expect(controller.getShipped).not.toHaveBeenCalledWith(expectedResult); }); }); diff --git a/modules/ticket/front/basic-data/step-three/index.html b/modules/ticket/front/basic-data/step-three/index.html index d698a180f..300b8f6d3 100644 --- a/modules/ticket/front/basic-data/step-three/index.html +++ b/modules/ticket/front/basic-data/step-three/index.html @@ -1,5 +1,5 @@ - + { describe('onSubmit()', () => { it(`should return an error if the item doesn't have option property in the controller`, () => { - controller.ticket = {}; - + controller._ticket = {id: 1}; controller.onSubmit(); expect(vnApp.showError).toHaveBeenCalledWith('Choose an option'); diff --git a/modules/ticket/front/basic-data/step-two/index.html b/modules/ticket/front/basic-data/step-two/index.html index aa5a23212..04cb7f212 100644 --- a/modules/ticket/front/basic-data/step-two/index.html +++ b/modules/ticket/front/basic-data/step-two/index.html @@ -1,42 +1,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        ItemDescriptionQuantityPrice (PPU)New price (PPU)Price difference
        {{("000000"+sale.itemFk).slice(-6)}} - - - {{::sale.quantity}}{{::sale.price | currency: 'EUR': 2}}{{::sale.component.newPrice | currency: 'EUR': 2}}{{::sale.component.difference | currency: 'EUR': 2}}
        {{$ctrl.totalPrice | currency: 'EUR': 2}}{{$ctrl.totalNewPrice | currency: 'EUR': 2}}{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}
        -
        -
        - + + + + + Item + Description + Quantity + Price (PPU) + New price (PPU) + Price difference + + + + + {{("000000"+sale.itemFk).slice(-6)}} + + + + + {{::sale.quantity}} + {{::sale.price | currency: 'EUR': 2}} + {{::sale.component.newPrice | currency: 'EUR': 2}} + {{::sale.component.difference | currency: 'EUR': 2}} + + + + + + + + {{$ctrl.totalPrice | currency: 'EUR': 2}} + {{$ctrl.totalNewPrice | currency: 'EUR': 2}} + {{$ctrl.totalPriceDifference | currency: 'EUR': 2}} + + + + diff --git a/modules/ticket/front/basic-data/step-two/index.js b/modules/ticket/front/basic-data/step-two/index.js index 1446ac171..a481afefc 100644 --- a/modules/ticket/front/basic-data/step-two/index.js +++ b/modules/ticket/front/basic-data/step-two/index.js @@ -7,6 +7,17 @@ class Controller { $onInit() { this.data.registerChild(this); + } + + get ticket() { + return this._ticket; + } + + set ticket(value) { + this._ticket = value; + + if (!value) return; + this.getTotalPrice(); this.getTotalNewPrice(); this.getTotalDifferenceOfPrice(); diff --git a/modules/ticket/front/card/index.html b/modules/ticket/front/card/index.html index c55064ce6..a7c70c484 100644 --- a/modules/ticket/front/card/index.html +++ b/modules/ticket/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/ticket/front/card/index.js b/modules/ticket/front/card/index.js index b805c3803..131968d65 100644 --- a/modules/ticket/front/card/index.js +++ b/modules/ticket/front/card/index.js @@ -1,18 +1,23 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $state) { - this.$http = $http; - this.$state = $state; - this.filter = { +class Controller extends ModuleCard { + reload() { + let filter = { include: [ - {relation: 'warehouse', scope: {fields: ['name']}}, - {relation: 'invoiceOut', scope: {fields: ['id']}}, {relation: 'address'}, {relation: 'ship'}, - {relation: 'agencyMode', scope: {fields: ['name']}}, {relation: 'stowaway'}, { + relation: 'warehouse', + scope: {fields: ['name']} + }, { + relation: 'invoiceOut', + scope: {fields: ['id']} + }, { + relation: 'agencyMode', + scope: {fields: ['name']} + }, { relation: 'client', scope: { fields: [ @@ -37,8 +42,7 @@ class Controller { } }, }, - }, - { + }, { relation: 'state', scope: { fields: ['stateFk'], @@ -50,37 +54,18 @@ class Controller { }, ], }; + + this.$http.get(`Tickets/${this.$params.id}`, {filter}) + .then(res => this.onData(res.data)); } - $onInit() { - this.getCard(); - } - - getCard() { - const json = encodeURIComponent(JSON.stringify(this.filter)); - const query = `Tickets/${this.$state.params.id}?filter=${json}`; - this.$http.get(query).then(res => { - if (res.data) { - this.ticket = res.data; - this.getDebt(res.data.client.id); - } - }); - } - - getDebt(id) { - const query = `Clients/${id}/getDebt`; - this.$http.get(query).then(res => { - if (res.data) - this.ticket.client.debt = res.data.debt; - }); - } - reload() { - this.getCard(); + onData(data) { + this.ticket = data; + this.$http.get(`Clients/${data.client.id}/getDebt`) + .then(res => this.ticket.client.debt = res.data.debt); } } -Controller.$inject = ['$http', '$state']; - ngModule.component('vnTicketCard', { template: require('./index.html'), controller: Controller diff --git a/modules/ticket/front/card/index.spec.js b/modules/ticket/front/card/index.spec.js index 9df76aaff..33757860c 100644 --- a/modules/ticket/front/card/index.spec.js +++ b/modules/ticket/front/card/index.spec.js @@ -3,34 +3,31 @@ import './index'; describe('Ticket', () => { describe('Component vnTicketCard', () => { let controller; - let $state; let $httpBackend; + let data = { + id: 1, + client: {name: 'fooName'} + }; + let client = {debt: 10.5}; beforeEach(ngModule('ticket')); - beforeEach(angular.mock.inject(($componentController, _$state_, _$httpBackend_) => { - $state = _$state_; - $state.params.id = 1; + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $stateParams) => { $httpBackend = _$httpBackend_; - controller = $componentController('vnTicketCard', {$state}); + + let $element = angular.element('
        '); + controller = $componentController('vnTicketCard', {$element}); + + $stateParams.id = data.id; + $httpBackend.whenRoute('GET', 'Tickets/:id').respond(data); + $httpBackend.whenRoute('GET', 'Clients/:id/getDebt').respond(client); })); - describe('getCard()', () => { - it('should perform a GET query and define the ticket property on controller', () => { - let filter = controller.filter; + it('should request data and set it on the controller', () => { + controller.reload(); + $httpBackend.flush(); - filter = encodeURIComponent(JSON.stringify(filter)); - - $httpBackend.when('GET', `Tickets/1?filter=${filter}`).respond({id: 1, client: {id: 1}}); - $httpBackend.expect('GET', `Tickets/1?filter=${filter}`); - $httpBackend.when('GET', `Clients/1/getDebt`).respond(); - $httpBackend.expect('GET', `Clients/1/getDebt`); - controller.getCard(); - $httpBackend.flush(); - - expect(controller.ticket).toBeDefined(); - expect(controller.ticket.id).toEqual(1); - }); + expect(controller.ticket).toMatchObject(data); }); }); }); diff --git a/modules/ticket/front/create/card.js b/modules/ticket/front/create/card.js index 7ee4c1b4e..629647416 100644 --- a/modules/ticket/front/create/card.js +++ b/modules/ticket/front/create/card.js @@ -86,14 +86,18 @@ class Controller { } getAvailableAgencies() { - if (this.ticket.warehouseFk && this.ticket.addressFk && this.ticket.landed && this.ticket.clientFk) { - this.ticket.agencyModeFk = null; - let filter = {warehouseFk: this.ticket.warehouseFk, addressFk: this.ticket.addressFk, landed: this.ticket.landed}; - filter = encodeURIComponent(JSON.stringify(filter)); - let query = `Agencies/getAgenciesWithWarehouse?filter=${filter}`; - this.$http.get(query).then(res => { - this._availableAgencies = res.data[0]; - }); + let ticket = this.ticket; + let params = { + warehouseFk: ticket.warehouseFk, + addressFk: ticket.addressFk, + landed: ticket.landed + }; + + if (params.warehouseFk && params.addressFk && params.landed) { + ticket.agencyModeFk = null; + + this.$http.get(`Agencies/getAgenciesWithWarehouse`, {params}) + .then(res => this._availableAgencies = res.data); } } diff --git a/modules/ticket/front/create/index.html b/modules/ticket/front/create/index.html index 97980d08a..3ec562752 100644 --- a/modules/ticket/front/create/index.html +++ b/modules/ticket/front/create/index.html @@ -1,11 +1,9 @@ -
        -
        - - - - - - - -
        +
        + + + + + + +
        \ No newline at end of file diff --git a/modules/ticket/front/descriptor/index.js b/modules/ticket/front/descriptor/index.js index d37d2d42b..9cb320a31 100644 --- a/modules/ticket/front/descriptor/index.js +++ b/modules/ticket/front/descriptor/index.js @@ -40,7 +40,7 @@ class Controller extends Component { showChangeShipped() { if (!this.isEditable) { - this.vnApp.showError(this.$translate.instant('This ticket can\'t be modified')); + this.vnApp.showError(this.$translate.instant(`This ticket can't be modified`)); return; } this.newShipped = this.ticket.shipped; diff --git a/modules/ticket/front/dms/create/index.html b/modules/ticket/front/dms/create/index.html index 8c0b934b5..8e6af2b87 100644 --- a/modules/ticket/front/dms/create/index.html +++ b/modules/ticket/front/dms/create/index.html @@ -7,7 +7,7 @@ ng-submit="$ctrl.onSubmit()" class="vn-ma-md" enctype="multipart/form-data"> -
        +
        diff --git a/modules/ticket/front/dms/edit/index.html b/modules/ticket/front/dms/edit/index.html index a5e485dce..cf9af46d0 100644 --- a/modules/ticket/front/dms/edit/index.html +++ b/modules/ticket/front/dms/edit/index.html @@ -7,7 +7,7 @@ ng-submit="$ctrl.onSubmit()" class="vn-ma-md" enctype="multipart/form-data"> -
        +
        diff --git a/modules/ticket/front/index.js b/modules/ticket/front/index.js index f9c1e9d1c..f8433b5fe 100644 --- a/modules/ticket/front/index.js +++ b/modules/ticket/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './card'; diff --git a/modules/ticket/front/index/index.html b/modules/ticket/front/index/index.html index 1680d94d5..b303c3ae7 100644 --- a/modules/ticket/front/index/index.html +++ b/modules/ticket/front/index/index.html @@ -6,146 +6,142 @@ data="tickets" order="shipped DESC, zoneHour ASC, zoneMinute ASC, clientFk"> - - + + diff --git a/modules/ticket/front/index/index.js b/modules/ticket/front/index/index.js index 2a2f3a238..9cf1c5483 100644 --- a/modules/ticket/front/index/index.js +++ b/modules/ticket/front/index/index.js @@ -10,13 +10,14 @@ export default class Controller { this.$state = $state; this.selectedTicket = null; this.moreOptions = [ - {callback: () => { - this.$state.go('ticket.weekly.index'); - }, name: 'Weekly tickets', always: true}, - {callback: () => { - this.setBalanceCreateDialog(); - this.$.balanceCreateDialog.show(); - }, name: 'Payment on account...', always: true} + { + name: 'Payment on account...', + always: true, + callback: () => { + this.setBalanceCreateDialog(); + this.$.balanceCreateDialog.show(); + } + } ]; } diff --git a/modules/ticket/front/main/index.html b/modules/ticket/front/main/index.html new file mode 100644 index 000000000..7e058f45d --- /dev/null +++ b/modules/ticket/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/ticket/front/main/index.js b/modules/ticket/front/main/index.js new file mode 100644 index 000000000..d6100daa3 --- /dev/null +++ b/modules/ticket/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Ticket extends ModuleMain {} + +ngModule.vnComponent('vnTicket', { + controller: Ticket, + template: require('./index.html') +}); diff --git a/modules/ticket/front/note/index.html b/modules/ticket/front/note/index.html index be2e683ce..617cf2f7d 100644 --- a/modules/ticket/front/note/index.html +++ b/modules/ticket/front/note/index.html @@ -16,7 +16,7 @@ data="observations" form="form"> -
        + diff --git a/modules/ticket/front/request/create/index.html b/modules/ticket/front/request/create/index.html index 9d7395312..46d96ca44 100644 --- a/modules/ticket/front/request/create/index.html +++ b/modules/ticket/front/request/create/index.html @@ -6,7 +6,7 @@ save="post"> -
        +
        - + @@ -294,7 +294,7 @@

        Destination ticket

        diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js index 6df328d88..fb940a8ef 100644 --- a/modules/ticket/front/sale/index.js +++ b/modules/ticket/front/sale/index.js @@ -32,6 +32,11 @@ class Controller { callback: this.createClaim, show: () => this.isEditable }, + { + name: 'Recalculate price', + callback: this.calculateSalePrice, + show: () => this.hasOneSaleSelected() + }, ]; this._sales = []; this.imagesPath = '//verdnatura.es/vn-image-data/catalog'; @@ -534,6 +539,21 @@ class Controller { this.isEditable = res.data; }); } + + hasOneSaleSelected() { + if (this.totalCheckedLines() === 1) + return true; + return false; + } + + calculateSalePrice() { + const sale = this.checkedLines()[0]; + const query = `Sales/${sale.id}/calculate`; + this.$http.post(query).then(res => { + this.vnApp.showSuccess(this.$translate.instant('Data saved!')); + this.$scope.model.refresh(); + }); + } } Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate']; diff --git a/modules/ticket/front/sale/locale/es.yml b/modules/ticket/front/sale/locale/es.yml index 9deb3f06a..f381eb6e6 100644 --- a/modules/ticket/front/sale/locale/es.yml +++ b/modules/ticket/front/sale/locale/es.yml @@ -29,4 +29,5 @@ SMSAvailability: >- {{notAvailables}} no disponible/s. Disculpe las molestias. Continue anyway?: ¿Continuar de todas formas? This ticket is now empty: El ticket ha quedado vacio -Do you want to delete it?: ¿Quieres borrarlo? \ No newline at end of file +Do you want to delete it?: ¿Quieres borrarlo? +Recalculate price: Recalcular precio \ No newline at end of file diff --git a/modules/ticket/front/sale/specs/index.spec.js b/modules/ticket/front/sale/specs/index.spec.js index cd0e322a5..0606d81a4 100644 --- a/modules/ticket/front/sale/specs/index.spec.js +++ b/modules/ticket/front/sale/specs/index.spec.js @@ -1,5 +1,6 @@ import '../index.js'; import watcher from 'core/mocks/watcher'; +import crudModel from 'core/mocks/crud-model'; describe('Ticket', () => { describe('Component vnTicketSale', () => { @@ -40,6 +41,7 @@ describe('Ticket', () => { $scope.watcher = watcher; $scope.sms = {open: () => {}}; $scope.ticket = ticket; + $scope.model = crudModel; $httpBackend = _$httpBackend_; Object.defineProperties($state.params, { id: { @@ -334,5 +336,27 @@ describe('Ticket', () => { expect(window.open).toHaveBeenCalledWith('/somePath', '_blank'); }); }); + + describe('hasOneSaleSelected()', () => { + it('should return true if just one sale is selected', () => { + controller.sales[0].checked = true; + + expect(controller.hasOneSaleSelected()).toBeTruthy(); + }); + }); + + describe('calculateSalePrice()', () => { + it('should make an HTTP post query ', () => { + controller.sales[0].checked = true; + + $httpBackend.when('POST', `Sales/4/calculate`).respond(200); + $httpBackend.whenGET(`Tickets/1/subtotal`).respond(200, 227.5); + $httpBackend.whenGET(`Tickets/1/getVAT`).respond(200, 10.5); + $httpBackend.whenGET(`Tickets/1/isEditable`).respond(); + + controller.calculateSalePrice(); + $httpBackend.flush(); + }); + }); }); }); diff --git a/modules/ticket/front/services/index.js b/modules/ticket/front/services/index.js index c3d683d03..e45381940 100644 --- a/modules/ticket/front/services/index.js +++ b/modules/ticket/front/services/index.js @@ -17,19 +17,11 @@ class Controller { } getDefaultTaxClass() { - let config = {params: { - filter: { - where: { - code: 'G' - } - } - }}; - - let query = 'TaxClasses/findOne'; - this.$http.get(query, config).then(res => { - if (res.data) - this.defaultTaxClass = res.data; - }); + let filter = { + where: {code: 'G'} + }; + this.$http.get('TaxClasses/findOne', {filter}) + .then(res => this.defaultTaxClass = res.data); } add() { diff --git a/modules/ticket/front/services/index.spec.js b/modules/ticket/front/services/index.spec.js index b3c4adba5..b94b3e57f 100644 --- a/modules/ticket/front/services/index.spec.js +++ b/modules/ticket/front/services/index.spec.js @@ -19,17 +19,12 @@ describe('Ticket component vnTicketService', () => { describe('getDefaultTaxClass', () => { it('should set the default tax class in the controller', () => { - const config = { - filter: { - where: { - code: 'G' - } - } - }; - let serializedParams = $httpParamSerializer(config); - - $httpBackend.when('GET', `TaxClasses/findOne?${serializedParams}`).respond({id: 4000, name: 'Whatever', code: 'GG'}); - $httpBackend.expect('GET', `TaxClasses/findOne?${serializedParams}`); + $httpBackend.whenRoute('GET', `TaxClasses/findOne`) + .respond({ + id: 4000, + name: 'Whatever', + code: 'GG' + }); controller.getDefaultTaxClass(); $httpBackend.flush(); diff --git a/modules/ticket/front/tracking/edit/index.html b/modules/ticket/front/tracking/edit/index.html index 61647f669..3a1b13e50 100644 --- a/modules/ticket/front/tracking/edit/index.html +++ b/modules/ticket/front/tracking/edit/index.html @@ -4,7 +4,7 @@ data="$ctrl.params" form="form"> - + -
        - - - + + + + + + + + + + + + Ticket ID + Client + Weekday + Warehouse + Salesperson + + + + + + + + {{weekly.ticketFk}} + + + + + {{::weekly.clientName}} + + + + + + + {{::weekly.warehouseName}} + + + {{::weekly.nickName}} + + + + + + + + + - - - - - - Ticket ID - Client - Weekday - Warehouse - Salesperson - - - - - - - - {{weekly.ticketFk}} - - - - - {{::weekly.clientName}} - - - - - - - {{::weekly.warehouseName}} - - - {{::weekly.nickName}} - - - - - - - - - - - -
        + @@ -96,4 +93,4 @@ on-response="$ctrl.returnDialog($response)" question="This ticket will be removed from weekly tickets! Continue anyway?" message="You are going to delete this weekly ticket"> - \ No newline at end of file + diff --git a/modules/travel/back/models/entry.json b/modules/travel/back/models/entry.json index 23c88091a..7d20ffcbd 100644 --- a/modules/travel/back/models/entry.json +++ b/modules/travel/back/models/entry.json @@ -30,8 +30,11 @@ "isConfirmed": { "type": "Boolean" }, - "isRaid": { - "type": "Boolean" + "isVirtual": { + "type": "Boolean", + "mysql": { + "columnName": "isRaid" + } }, "commission": { "type": "Number" diff --git a/modules/travel/front/basic-data/index.html b/modules/travel/front/basic-data/index.html index 557a243c7..85a5fad2b 100644 --- a/modules/travel/front/basic-data/index.html +++ b/modules/travel/front/basic-data/index.html @@ -5,7 +5,7 @@ form="form" save="patch"> - + - - - - -
        - + + + + + diff --git a/modules/travel/front/card/index.js b/modules/travel/front/card/index.js index 40d3c0f51..c3ad41702 100644 --- a/modules/travel/front/card/index.js +++ b/modules/travel/front/card/index.js @@ -1,20 +1,16 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -export default class Controller { - constructor($stateParams, $http) { - this.$http = $http; - this.$stateParams = $stateParams; - this.travel = null; - this.filter = { - where: {id: $stateParams.id}, +class Controller extends ModuleCard { + reload() { + let filter = { include: [ { relation: 'warehouseIn', scope: { fields: ['id', 'name'] } - }, - { + }, { relation: 'warehouseOut', scope: { fields: ['id', 'name'] @@ -22,24 +18,11 @@ export default class Controller { } ] }; - } - $onInit() { - this.getCard(); - } - - getCard() { - const params = {filter: this.filter}; - this.$http.get(`Travels/${this.$stateParams.id}`, {params}).then(response => { - this.travel = response.data; - }); - } - - reload() { - this.getCard(); + this.$http.get(`Travels/${this.$params.id}`, {filter}) + .then(response => this.travel = response.data); } } -Controller.$inject = ['$stateParams', '$http']; ngModule.component('vnTravelCard', { template: require('./index.html'), diff --git a/modules/travel/front/descriptor/index.js b/modules/travel/front/descriptor/index.js index 0739c4485..187a7ed8f 100644 --- a/modules/travel/front/descriptor/index.js +++ b/modules/travel/front/descriptor/index.js @@ -14,7 +14,7 @@ ngModule.component('vnTravelDescriptor', { travel: '<' }, require: { - card: '^vnTravelCard' + card: '^?vnTravelCard' }, controller: Controller }); diff --git a/modules/travel/front/index.js b/modules/travel/front/index.js index 1017a431b..d9e1fa07e 100644 --- a/modules/travel/front/index.js +++ b/modules/travel/front/index.js @@ -1,5 +1,6 @@ export * from './module'; +import './main'; import './index/'; import './search-panel'; import './descriptor'; diff --git a/modules/travel/front/index/index.html b/modules/travel/front/index/index.html index dc24e2738..1989a1262 100644 --- a/modules/travel/front/index/index.html +++ b/modules/travel/front/index/index.html @@ -1,65 +1,58 @@ - - + diff --git a/modules/travel/front/main/index.html b/modules/travel/front/main/index.html new file mode 100644 index 000000000..71b9462fe --- /dev/null +++ b/modules/travel/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/travel/front/main/index.js b/modules/travel/front/main/index.js new file mode 100644 index 000000000..3be713973 --- /dev/null +++ b/modules/travel/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Travel extends ModuleMain {} + +ngModule.vnComponent('vnTravel', { + controller: Travel, + template: require('./index.html') +}); diff --git a/modules/travel/front/routes.json b/modules/travel/front/routes.json index 55643c937..f540d532f 100644 --- a/modules/travel/front/routes.json +++ b/modules/travel/front/routes.json @@ -1,17 +1,24 @@ { "module": "travel", "name": "Travels", + "icon": "local_airport", "validations": true, "dependencies": ["worker"], - "menu": [ - {"state": "travel.card.basicData", "icon": "settings"}, - {"state": "travel.card.log", "icon": "history"}], + "menus": { + "main": [ + {"state": "travel.index", "icon": "local_airport"} + ], + "card": [ + {"state": "travel.card.basicData", "icon": "settings"}, + {"state": "travel.card.log", "icon": "history"} + ] + }, "routes": [ { "url": "/travel", "state": "travel", "abstract": true, - "component": "ui-view", + "component": "vn-travel", "description": "Travels" }, { "url": "/index?q", diff --git a/modules/worker/back/methods/worker-calendar/absences.js b/modules/worker/back/methods/worker-calendar/absences.js index e822291b7..541f95ff5 100644 --- a/modules/worker/back/methods/worker-calendar/absences.js +++ b/modules/worker/back/methods/worker-calendar/absences.js @@ -108,10 +108,18 @@ module.exports = Self => { } }); - // Get number of total holidays + // Get number of worked days + let workedDays = 0; contracts.forEach(contract => { - calendar.totalHolidays += getHolidaysByContract(contract, yearEnded); + const started = contract.started; + const ended = contract.ended; + const startedTime = started.getTime(); + const endedTime = ended && ended.getTime() || yearEnded; + const dayTimestamp = 1000 * 60 * 60 * 24; + workedDays += Math.floor((endedTime - startedTime) / dayTimestamp); + + // Workcenter holidays let holidayList = contract.workCenter().holidays(); for (let day of holidayList) { day.dated = new Date(day.dated); @@ -120,26 +128,19 @@ module.exports = Self => { holidays.push(day); } }); + const currentContract = contracts.find(contract => { + return contract.started <= new Date() + && (contract.ended >= new Date() || contract.ended == null); + }); + + if (currentContract) { + const maxDays = currentContract.holidays().days; + calendar.totalHolidays = maxDays; + + if (workedDays < 365) + calendar.totalHolidays = Math.round(2 * maxDays * (workedDays + 1) / 365) / 2; + } return [calendar, absences, holidays]; }; - - function getHolidaysByContract(contract, endOfYear) { - const dayTimestamp = 1000 * 60 * 60 * 24; - - const started = contract.started; - const ended = contract.ended; - const startedTime = started.getTime(); - const endedTime = ended && ended.getTime() || endOfYear; - - const contractDays = Math.floor((endedTime - startedTime) / dayTimestamp); - - if (contractDays < 365) { - let holidays = Math.round(2 * contract.holidays().days * (contractDays + 1) / 365) / 2; - - return holidays; - } - - return contract.holidays().days; - } }; diff --git a/modules/worker/back/methods/worker-calendar/specs/absences.spec.js b/modules/worker/back/methods/worker-calendar/specs/absences.spec.js index ccafa683d..0a5e92e1f 100644 --- a/modules/worker/back/methods/worker-calendar/specs/absences.spec.js +++ b/modules/worker/back/methods/worker-calendar/specs/absences.spec.js @@ -43,13 +43,13 @@ describe('Worker absences()', () => { expect(sixthType).toEqual('Holidays'); }); - it(`should fire the worker 106 on Juny and see he/she has 14`, async() => { + it(`should see he/she has 26.5`, async() => { let workerFk = 106; const firedWorker = await app.models.WorkerLabour.findById(workerFk); const endedDate = new Date(); - endedDate.setDate(30); - endedDate.setMonth(5); + endedDate.setDate(endedDate.getDate() + 1); + endedDate.setMonth(endedDate.getMonth() + 1); endedDate.setHours(0, 0, 0, 0); await firedWorker.updateAttributes({ended: endedDate}); @@ -71,7 +71,7 @@ describe('Worker absences()', () => { let calendar = result[0]; let absences = result[1]; - expect(calendar.totalHolidays).toEqual(13.5); + expect(calendar.totalHolidays).toEqual(26.5); expect(calendar.holidaysEnjoyed).toEqual(5); let firstType = absences[0].absenceType().name; @@ -81,13 +81,13 @@ describe('Worker absences()', () => { expect(sixthType).toEqual('Holidays'); }); - it(`should fire the worker 106 on March and see he/she has 7`, async() => { + it(`should see he/she has 26.5`, async() => { const firedWorker = await app.models.WorkerLabour.findById(106); const endedDate = new Date(); + endedDate.setDate(endedDate.getDate() + 1); + endedDate.setMonth(endedDate.getMonth() + 1); endedDate.setHours(0, 0, 0, 0); - endedDate.setMonth(2); - endedDate.setDate(31); await firedWorker.updateAttributes({ended: endedDate}); @@ -109,7 +109,7 @@ describe('Worker absences()', () => { let calendar = result[0]; let absences = result[1]; - expect(calendar.totalHolidays).toEqual(7); + expect(calendar.totalHolidays).toEqual(26.5); expect(calendar.holidaysEnjoyed).toEqual(5); let firstType = absences[0].absenceType().name; @@ -119,19 +119,13 @@ describe('Worker absences()', () => { expect(sixthType).toEqual('Holidays'); }); - it(`should fire the worker 106 on january and see he/she has x`, async() => { - const firedWorker = await app.models.WorkerLabour.findById(106); - - const endedDate = new Date(); - endedDate.setHours(0, 0, 0, 0); - endedDate.setMonth(0); - endedDate.setDate(28); - - await firedWorker.updateAttributes({ended: endedDate}); - + it(`should see he/she has 27.5`, async() => { let ctx = {req: {accessToken: {userId: 106}}}; let workerFk = 106; + const firedWorker = await app.models.WorkerLabour.findById(106); + await firedWorker.updateAttributes({ended: null}); + const started = new Date(); started.setHours(0, 0, 0, 0); started.setMonth(0); @@ -147,7 +141,7 @@ describe('Worker absences()', () => { let calendar = result[0]; let absences = result[1]; - expect(calendar.totalHolidays).toEqual(2); + expect(calendar.totalHolidays).toEqual(27.5); expect(calendar.holidaysEnjoyed).toEqual(5); let firstType = absences[0].absenceType().name; diff --git a/modules/worker/back/methods/worker-time-control/addTime.js b/modules/worker/back/methods/worker-time-control/addTimeEntry.js similarity index 91% rename from modules/worker/back/methods/worker-time-control/addTime.js rename to modules/worker/back/methods/worker-time-control/addTimeEntry.js index 8130a16fd..649364151 100644 --- a/modules/worker/back/methods/worker-time-control/addTime.js +++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js @@ -1,7 +1,7 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { - Self.remoteMethodCtx('addTime', { + Self.remoteMethodCtx('addTimeEntry', { description: 'Adds a new hour registry', accessType: 'WRITE', accepts: [{ @@ -16,12 +16,12 @@ module.exports = Self => { root: true }], http: { - path: `/addTime`, + path: `/addTimeEntry`, verb: 'POST' } }); - Self.addTime = async(ctx, data) => { + Self.addTimeEntry = async(ctx, data) => { const Worker = Self.app.models.Worker; const myUserId = ctx.req.accessToken.userId; const myWorker = await Worker.findOne({where: {userFk: myUserId}}); diff --git a/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js b/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js new file mode 100644 index 000000000..540a7ab8e --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js @@ -0,0 +1,41 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethodCtx('deleteTimeEntry', { + description: 'Deletes a manual time entry for a worker if the user role is above the worker', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The time entry id', + http: {source: 'path'} + }], + returns: { + type: 'boolean', + root: true + }, + http: { + path: `/:id/deleteTimeEntry`, + verb: 'POST' + } + }); + + Self.deleteTimeEntry = async(ctx, id) => { + const currentUserId = ctx.req.accessToken.userId; + const workerModel = Self.app.models.Worker; + + const targetTimeEntry = await Self.findById(id); + + const isSubordinate = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk); + const isHHRR = await Self.app.models.Account.hasRole(currentUserId, 'hr'); + + const notAllowed = isSubordinate === false || (isSubordinate && currentUserId == targetTimeEntry.userFk && !isHHRR); + + if (notAllowed) + throw new UserError(`You don't have enough privileges`); + + return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [ + targetTimeEntry.userFk, targetTimeEntry.timed]); + }; +}; diff --git a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js new file mode 100644 index 000000000..bcc15225a --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js @@ -0,0 +1,99 @@ +const app = require('vn-loopback/server/server'); + +describe('workerTimeControl addTimeEntry()', () => { + let insertedTime; + + it('should fail to add a time entry if the target user is not a subordinate', async() => { + let error; + let ctx = {req: {accessToken: {userId: 1}}}; + let data = { + workerFk: 2, + timed: new Date() + }; + + try { + await app.models.WorkerTimeControl.addTimeEntry(ctx, data); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.statusCode).toBe(400); + expect(error.message).toBe(`You don't have enough privileges`); + }); + + it('should fail to add if the current and the target user are the same and is not team boss', async() => { + let error; + let ctx = {req: {accessToken: {userId: 1}}}; + let data = { + workerFk: 1, + timed: new Date() + }; + + try { + await app.models.WorkerTimeControl.addTimeEntry(ctx, data); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.statusCode).toBe(400); + expect(error.message).toBe(`You don't have enough privileges`); + }); + + it('should add if the current user is team boss and the target user is a subordinate', async() => { + todayAtSix = new Date(); + todayAtSix.setHours(18, 30, 0, 0); + + let teamBossId = 13; + let ctx = {req: {accessToken: {userId: teamBossId}}}; + let data = { + workerFk: teamBossId, + timed: todayAtSix + }; + + await app.models.WorkerTimeControl.addTimeEntry(ctx, data); + + insertedTime = await app.models.WorkerTimeControl.findOne({where: {timed: data.timed}}); + + let createdTimeEntry = await app.models.WorkerTimeControl.findById(insertedTime.id); + + expect(createdTimeEntry).toBeDefined(); + }); + + it('should try but fail to delete the created time entry for the team boss as team boss', async() => { + let error; + let teamBossId = 13; + let ctx = {req: {accessToken: {userId: teamBossId}}}; + + let createdTimeEntry = await app.models.WorkerTimeControl.findById(insertedTime.id); + + expect(createdTimeEntry).toBeDefined(); + + try { + await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.statusCode).toBe(400); + expect(error.message).toBe(`You don't have enough privileges`); + }); + + it('should delete the created time entry for the team boss as HHRR', async() => { + let HHRRId = 37; + let ctx = {req: {accessToken: {userId: HHRRId}}}; + + let createdTimeEntry = await app.models.WorkerTimeControl.findById(insertedTime.id); + + expect(createdTimeEntry).toBeDefined(); + + ctx.req.accessToken.userId = HHRRId; + await app.models.WorkerTimeControl.deleteTimeEntry(ctx, insertedTime.id); + + createdTimeEntry = await app.models.WorkerTimeControl.findById(insertedTime.id); + + expect(createdTimeEntry).toBeNull(); + }); +}); diff --git a/modules/worker/back/methods/worker/getWorkedHours.js b/modules/worker/back/methods/worker/getWorkedHours.js new file mode 100644 index 000000000..e96d30f8d --- /dev/null +++ b/modules/worker/back/methods/worker/getWorkedHours.js @@ -0,0 +1,67 @@ +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('getWorkedHours', { + description: 'returns the total worked hours per day for a given range of dates in format YYYY-mm-dd hh:mm:ss', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The worker id', + http: {source: 'path'} + }, + { + arg: 'from', + type: 'Date', + required: true, + description: `The from date` + }, + { + arg: 'to', + type: 'Date', + required: true, + description: `The to date` + }], + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/:id/getWorkedHours`, + verb: 'GET' + } + }); + + Self.getWorkedHours = async(id, from, to) => { + const conn = Self.dataSource.connector; + const stmts = []; + + let worker = await Self.app.models.Worker.findById(id); + let userId = worker.userFk; + + stmts.push(` + DROP TEMPORARY TABLE IF EXISTS + tmp.timeControlCalculate, + tmp.timeBusinessCalculate + `); + + stmts.push(new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [userId, from, to])); + stmts.push(new ParameterizedSQL('CALL vn.timeBusiness_calculateByUser(?, ?, ?)', [userId, from, to])); + let resultIndex = stmts.push(` + SELECT tbc.dated, tbc.timeWorkSeconds expectedHours, tcc.timeWorkSeconds workedHours + FROM tmp.timeBusinessCalculate tbc + LEFT JOIN tmp.timeControlCalculate tcc ON tcc.dated = tbc.dated + `) - 1; + stmts.push(` + DROP TEMPORARY TABLE IF EXISTS + tmp.timeControlCalculate, + tmp.timeBusinessCalculate + `); + + let sql = ParameterizedSQL.join(stmts, ';'); + let result = await conn.executeStmt(sql); + + return result[resultIndex]; + }; +}; diff --git a/modules/worker/back/methods/worker/sendMessage.js b/modules/worker/back/methods/worker/sendMessage.js deleted file mode 100644 index f3b4cd911..000000000 --- a/modules/worker/back/methods/worker/sendMessage.js +++ /dev/null @@ -1,170 +0,0 @@ -/* -Author : Enrique Blasco BLanquer -Date: 29 de octubre de 2019 -*/ -let request = require('request-promise-native'); -let UserError = require('vn-loopback/util/user-error'); -module.exports = Self => { - Self.remoteMethod('sendMessage', { - description: 'Send a RocketChat message', - accessType: 'WRITE', - accepts: [{ - arg: 'from', - type: 'String', - required: true, - description: 'user who sends the message' - }, { - arg: 'to', - type: 'String', - required: true, - description: 'user (@) or channel (#) to send the message' - }, { - arg: 'message', - type: 'String', - required: true, - description: 'The message' - }], - returns: { - type: 'boolean', - root: true - }, - http: { - path: `/sendMessage`, - verb: 'POST' - } - }); - - Self.sendMessage = async(from, to, message) => { - const rocketUser = await getRocketUser(); - const userId = rocketUser.data.userId; - const authToken = rocketUser.data.authToken; - if (to.includes('@')) return await sendUserMessage(to.replace('@', ''), userId, authToken, '@' + from + ' te ha mandado un mensaje: ' + message); - else return await sendChannelMessage(to.replace('#', ''), userId, authToken, '@' + from + ' dice: ' + message); - }; - - /** - * Returns a rocketchat token - * @return {Object} userId and authToken - */ - async function getRocketUser() { - const url = 'https://chat.verdnatura.es/api/v1/login'; - const options = { - method: 'POST', - uri: url, - body: { - user: 'VnBot', - password: 'Ub606cux7op.' - }, - headers: { - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } - - /** - * Send a user message - * @param {String} to user to send the message - * @param {String} userId rocket user id - * @param {String} authToken rocket token - * @param {String} message The message - * @return {Object} rocket info - */ - async function sendUserMessage(to, userId, authToken, message) { - const url = 'https://chat.verdnatura.es/api/v1/chat.postMessage'; - const options = { - method: 'POST', - uri: url, - body: { - 'channel': '@' + to, - 'text': message - }, - headers: { - 'X-Auth-Token': authToken, - 'X-User-Id': userId, - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } - - /** - * Send a channel message - * @param {String} to channel to send the message - * @param {String} userId rocket user id - * @param {String} authToken rocket token - * @param {String} message The message - * @return {Object} rocket info - */ - async function sendChannelMessage(to, userId, authToken, message) { - const channelInfo = await getChannelId(to, userId, authToken); - const url = 'https://chat.verdnatura.es/api/v1/chat.sendMessage'; - const channelId = channelInfo.channel._id; - - const options = { - method: 'POST', - uri: url, - body: { - 'message': { - 'rid': channelId, - 'msg': message - } - }, - headers: { - 'X-Auth-Token': authToken, - 'X-User-Id': userId, - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } - - /** - * Get channel id - * @param {String} to channel to get id - * @param {String} userId rocket user id - * @param {String} authToken rocket token - * @return {Object} rocket info - */ - async function getChannelId(to, userId, authToken) { - const url = 'https://chat.verdnatura.es/api/v1/channels.info?roomName=' + to; - const options = { - method: 'GET', - uri: url, - headers: { - 'X-Auth-Token': authToken, - 'X-User-Id': userId, - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } -}; diff --git a/modules/worker/back/methods/worker/timeControl.js b/modules/worker/back/methods/worker/timeControl.js new file mode 100644 index 000000000..bc88197fe --- /dev/null +++ b/modules/worker/back/methods/worker/timeControl.js @@ -0,0 +1,50 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('timeControl', { + description: 'Returns a range of worked hours for a given worker id', + accessType: 'READ', + accepts: [ + { + arg: 'id', + type: 'number', + required: true, + description: 'The worker id', + http: {source: 'path'} + }, + { + arg: 'dateFrom', + type: 'datetime', + required: true, + description: 'the date from the time control begins in format YYYY-mm-dd-hh:mm:ss' + }, + { + arg: 'dateTo', + type: 'datetime', + required: true, + description: 'the date when the time control finishes in format YYYY-mm-dd-hh:mm:ss' + }], + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/:id/timeControl`, + verb: 'GET' + } + }); + + Self.timeControl = async(id, from, to) => { + const conn = Self.dataSource.connector; + const stmts = []; + + stmts.push(new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [id, from, to])); + + let sql = ParameterizedSQL.join(stmts, ';'); + let result = await conn.executeStmt(sql); + + + return result[0]; + }; +}; diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js index 4a065f430..f0191c36f 100644 --- a/modules/worker/back/models/worker-time-control.js +++ b/modules/worker/back/models/worker-time-control.js @@ -2,7 +2,8 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { require('../methods/worker-time-control/filter')(Self); - require('../methods/worker-time-control/addTime')(Self); + require('../methods/worker-time-control/addTimeEntry')(Self); + require('../methods/worker-time-control/deleteTimeEntry')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') diff --git a/modules/worker/back/models/worker-time-control.json b/modules/worker/back/models/worker-time-control.json index bfd6a44aa..5212222bd 100644 --- a/modules/worker/back/models/worker-time-control.json +++ b/modules/worker/back/models/worker-time-control.json @@ -19,6 +19,9 @@ }, "order": { "type": "Number" + }, + "direction": { + "type": "string" } }, "relations": { @@ -26,6 +29,11 @@ "type": "belongsTo", "model": "Account", "foreignKey": "userFk" + }, + "worker": { + "type": "hasOne", + "model": "Worker", + "foreignKey": "userFk" }, "warehouse": { "type": "belongsTo", diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 2273aca2d..9abdeb737 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -2,5 +2,6 @@ module.exports = Self => { require('../methods/worker/filter')(Self); require('../methods/worker/mySubordinates')(Self); require('../methods/worker/isSubordinate')(Self); - require('../methods/worker/sendMessage')(Self); + require('../methods/worker/getWorkedHours')(Self); + require('../methods/worker/getWorkerInfo')(Self); }; diff --git a/modules/worker/front/account/index.html b/modules/worker/front/account/index.html index 59c788312..f51876a07 100644 --- a/modules/worker/front/account/index.html +++ b/modules/worker/front/account/index.html @@ -10,7 +10,7 @@ data="user" form="form"> - + diff --git a/modules/worker/front/basic-data/index.html b/modules/worker/front/basic-data/index.html index b44b9ec91..1d0d705e7 100644 --- a/modules/worker/front/basic-data/index.html +++ b/modules/worker/front/basic-data/index.html @@ -6,7 +6,7 @@ url="Workers" save="post"> - + diff --git a/modules/worker/front/calendar/index.html b/modules/worker/front/calendar/index.html index bcf4c44f2..08b3f469c 100644 --- a/modules/worker/front/calendar/index.html +++ b/modules/worker/front/calendar/index.html @@ -3,20 +3,18 @@ data="absenceTypes" auto-load="true"> -
        -
        - - - - -
        +
        + + + +
        @@ -34,12 +32,6 @@ {{absenceType.name}} - - - - My longer overflowing absence type in chip -
        \ No newline at end of file diff --git a/modules/worker/front/calendar/index.js b/modules/worker/front/calendar/index.js index 38c147ec7..1860f6bdd 100644 --- a/modules/worker/front/calendar/index.js +++ b/modules/worker/front/calendar/index.js @@ -100,7 +100,7 @@ class Controller { let dayNumber = element.firstElementChild; dayNumber.title = event.name; dayNumber.style.backgroundColor = event.color; - dayNumber.style.color = 'white'; + dayNumber.style.color = 'rgba(0, 0, 0, 0.7)'; } } Controller.$inject = ['$element', '$http']; diff --git a/modules/worker/front/card/index.html b/modules/worker/front/card/index.html index 80f1f7a61..ba9c8ec53 100644 --- a/modules/worker/front/card/index.html +++ b/modules/worker/front/card/index.html @@ -1,7 +1,5 @@ - - - - - -
        -
        + + + + + diff --git a/modules/worker/front/card/index.js b/modules/worker/front/card/index.js index e149cb0d2..dd2a24448 100644 --- a/modules/worker/front/card/index.js +++ b/modules/worker/front/card/index.js @@ -1,19 +1,8 @@ import ngModule from '../module'; +import ModuleCard from 'salix/components/module-card'; -class Controller { - constructor($http, $stateParams) { - Object.assign(this, { - $http, - $stateParams, - }); - } - - $onInit() { - this.reload(); - } - +class Controller extends ModuleCard { reload() { - let query = `Workers/${this.$stateParams.id}`; let filter = { include: [ { @@ -27,8 +16,7 @@ class Controller { } } } - }, - { + }, { relation: 'sip', scope: { fields: ['extension', 'secret'] @@ -50,14 +38,11 @@ class Controller { ] }; - this.$http.get(query, {params: {filter}}).then(res => { - this.worker = res.data; - }); + this.$http.get(`Workers/${this.$params.id}`, {filter}) + .then(res => this.worker = res.data); } } -Controller.$inject = ['$http', '$stateParams']; - ngModule.component('vnWorkerCard', { template: require('./index.html'), controller: Controller diff --git a/modules/worker/front/department/index.html b/modules/worker/front/department/index.html index a464d59e0..e77d11e12 100644 --- a/modules/worker/front/department/index.html +++ b/modules/worker/front/department/index.html @@ -3,32 +3,29 @@ url="departments/getLeaves" auto-load="false"> -
        - - - - {{::item.name}} - - - - - - -
        +
        + + + {{::item.name}} + + + + + +
        - - + diff --git a/modules/worker/front/index/index.js b/modules/worker/front/index/index.js index 6a1c2f65c..81e9070bd 100644 --- a/modules/worker/front/index/index.js +++ b/modules/worker/front/index/index.js @@ -7,9 +7,6 @@ export default class Controller { $, selectedWorker: null, }); - this.moreOptions = [ - {callback: () => this.$state.go('worker.department'), name: 'Departments'} - ]; } onSearch(params) { @@ -22,9 +19,10 @@ export default class Controller { preview(event, worker) { if (event.defaultPrevented) return; event.preventDefault(); + event.stopPropagation(); + this.selectedWorker = worker; this.$.preview.show(); - event.stopImmediatePropagation(); } onMoreChange(callback) { diff --git a/modules/worker/front/main/index.html b/modules/worker/front/main/index.html new file mode 100644 index 000000000..826a7905f --- /dev/null +++ b/modules/worker/front/main/index.html @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/worker/front/main/index.js b/modules/worker/front/main/index.js new file mode 100644 index 000000000..d97a2d636 --- /dev/null +++ b/modules/worker/front/main/index.js @@ -0,0 +1,9 @@ +import ngModule from '../module'; +import ModuleMain from 'salix/components/module-main'; + +export default class Worker extends ModuleMain {} + +ngModule.vnComponent('vnWorker', { + controller: Worker, + template: require('./index.html') +}); diff --git a/modules/worker/front/pbx/index.html b/modules/worker/front/pbx/index.html index 70e67d6f9..5118d38ed 100644 --- a/modules/worker/front/pbx/index.html +++ b/modules/worker/front/pbx/index.html @@ -2,7 +2,7 @@ vn-id="watcher" data="$ctrl.worker.sip"> -
        + diff --git a/modules/worker/front/phones/index.html b/modules/worker/front/phones/index.html index 92930615b..95e0366c1 100644 --- a/modules/worker/front/phones/index.html +++ b/modules/worker/front/phones/index.html @@ -12,7 +12,7 @@ vn-id="watcher" data="$ctrl.phones"> - + -
        - - - - - -
        {{::$ctrl.weekdayNames[$index].name}}
        + + + + + +
        {{::$ctrl.weekdayNames[$index].name}}
        +
        {{::weekday.dated | date: 'dd'}} {{::weekday.dated | date: 'MMMM'}} - - - - - - -
        - - - {{hour.timed | date: 'HH:mm'}} -
        -
        -
        -
        - - - - {{$ctrl.getWeekdayTotalHours(weekday)}} h. - - - - - - - - - - - - -
        -
        -
        Hours
        - - -
        - - +
        + + + +
        + {{::weekday.event.name}} +
        +
        + + + + + + +
        + + + + {{::hour.timed | date: 'HH:mm'}} + +
        +
        +
        +
        + + + + {{$ctrl.formatHours(weekday.workedHours)}} h. + + + + + + + + + + + + +
        +
        +
        Hours
        + + + +
        - -
        + + +
        + @@ -86,4 +111,10 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/modules/worker/front/time-control/index.js b/modules/worker/front/time-control/index.js index 930f4591c..c355138c3 100644 --- a/modules/worker/front/time-control/index.js +++ b/modules/worker/front/time-control/index.js @@ -1,12 +1,9 @@ +import Component from 'core/lib/component'; import ngModule from '../module'; import './style.scss'; - -class Controller { - constructor($scope, $http, $stateParams, $element, vnWeekDays) { - this.$stateParams = $stateParams; - this.$ = $scope; - this.$http = $http; - this.$element = $element; +class Controller extends Component { + constructor($element, $, vnWeekDays) { + super($element, $); this.weekDays = []; this.weekdayNames = vnWeekDays.locales; } @@ -34,9 +31,20 @@ class Controller { this.started = started; let ended = new Date(started.getTime()); - ended.setDate(ended.getDate() + 7); + ended.setHours(23, 59, 59, 59); + ended.setDate(ended.getDate() + 6); this.ended = ended; + this.weekDays = []; + let dayIndex = new Date(started.getTime()); + + while (dayIndex < ended) { + this.weekDays.push({ + dated: new Date(dayIndex.getTime()) + }); + dayIndex.setDate(dayIndex.getDate() + 1); + } + this.fetchHours(); } @@ -49,86 +57,118 @@ class Controller { set hours(value) { this._hours = value; - this.weekDays = []; - if (!this.hours) return; - let dayIndex = new Date(this.started.getTime()); - - while (dayIndex < this.ended) { - let weekDay = dayIndex.getDay(); - - let hours = this.hours - .filter(hour => new Date(hour.timed).getDay() == weekDay) - .sort((a, b) => new Date(a.timed) - new Date(b.timed)); - - this.weekDays.push({ - dated: new Date(dayIndex.getTime()), - hours - }); - dayIndex.setDate(dayIndex.getDate() + 1); + for (const weekDay of this.weekDays) { + if (value) { + let day = weekDay.dated.getDay(); + weekDay.hours = value + .filter(hour => new Date(hour.timed).getDay() == day) + .sort((a, b) => new Date(a.timed) - new Date(b.timed)); + } else + weekDay.hours = null; } } fetchHours() { - const params = {workerFk: this.$stateParams.id}; + const params = {workerFk: this.$params.id}; const filter = { where: {and: [ {timed: {gte: this.started}}, - {timed: {lt: this.ended}} + {timed: {lte: this.ended}} ]} }; - this.$.model.applyFilter(filter, params); + + this.getAbsences(); + this.getWorkedHours(this.started, this.ended); } hasEvents(day) { return day >= this.started && day < this.ended; } - hourColor(weekDay) { - return weekDay.manual ? 'alert' : 'warning'; + getWorkedHours(from, to) { + this.weekTotalHours = null; + let weekTotalHours = 0; + let params = { + id: this.$params.id, + from: from, + to: to + }; + + const query = `Workers/${this.$params.id}/getWorkedHours`; + return this.$http.get(query, {params}).then(res => { + const workDays = res.data; + const map = new Map(); + + for (const workDay of workDays) { + workDay.dated = new Date(workDay.dated); + map.set(workDay.dated, workDay); + weekTotalHours += workDay.workedHours; + } + + for (const weekDay of this.weekDays) { + const workDay = workDays.find(day => { + let from = new Date(day.dated); + from.setHours(0, 0, 0, 0); + + let to = new Date(day.dated); + to.setHours(23, 59, 59, 59); + + return weekDay.dated >= from && weekDay.dated <= to; + }); + + if (workDay) { + weekDay.expectedHours = workDay.expectedHours; + weekDay.workedHours = workDay.workedHours; + } + } + this.weekTotalHours = weekTotalHours; + }); } - getWeekdayTotalHours(weekday) { - if (weekday.hours.length == 0) return 0; + getFinishTime() { + if (!this.weekDays) return; - const hours = weekday.hours; + let today = new Date(); + today.setHours(0, 0, 0, 0); - let totalStamp = 0; + let todayInWeek = this.weekDays.find(day => day.dated.getTime() === today.getTime()); - hours.forEach((hour, index) => { - let currentHour = new Date(hour.timed); - let previousHour = new Date(hour.timed); + if (todayInWeek && todayInWeek.hours && todayInWeek.hours.length) { + const remainingTime = todayInWeek.workedHours ? ((todayInWeek.expectedHours - todayInWeek.workedHours) * 1000) : null; + const lastKnownEntry = todayInWeek.hours[todayInWeek.hours.length - 1]; + const lastKnownTime = new Date(lastKnownEntry.timed).getTime(); + const finishTimeStamp = lastKnownTime && remainingTime ? lastKnownTime + remainingTime : null; - if (index > 0 && (index % 2 == 1)) - previousHour = new Date(hours[index - 1].timed); + if (finishTimeStamp) { + let finishDate = new Date(finishTimeStamp); + let hour = finishDate.getHours(); + let minute = finishDate.getMinutes(); - const dif = Math.abs(previousHour - currentHour); + if (hour < 10) hour = `0${hour}`; + if (minute < 10) minute = `0${minute}`; - totalStamp += dif; - }); + return `${hour}:${minute} h.`; + } + } + } - if (totalStamp / 3600 / 1000 > 5) - totalStamp += (20 * 60 * 1000); + set weekTotalHours(totalHours) { + if (!totalHours) return this._weekTotalHours = this.formatHours(0); - weekday.total = totalStamp; - - return this.formatHours(totalStamp); + this._weekTotalHours = this.formatHours(totalHours); } get weekTotalHours() { - let total = 0; - - this.weekDays.forEach(weekday => { - if (weekday.total) - total += weekday.total; - }); - return this.formatHours(total); + return this._weekTotalHours; } formatHours(timestamp) { - let hour = Math.floor(timestamp / 3600 / 1000); - let min = Math.floor(timestamp / 60 / 1000 - 60 * hour); + timestamp = timestamp || 0; + + let hour = Math.floor(timestamp / 3600); + let min = Math.floor(timestamp / 60 - 60 * hour); if (hour < 10) hour = `0${hour}`; if (min < 10) min = `0${min}`; @@ -148,15 +188,76 @@ class Controller { addTime(response) { if (response !== 'accept') return; let data = { - workerFk: this.$stateParams.id, + workerFk: this.$params.id, timed: this.newTime }; - this.$http.post(`WorkerTimeControls/addTime`, data) + this.$http.post(`WorkerTimeControls/addTimeEntry`, data) .then(() => this.fetchHours()); } + + showDeleteDialog(hour) { + this.timeEntryToDelete = hour; + this.$.deleteEntryDialog.show(); + } + + deleteTimeEntry() { + const entryId = this.timeEntryToDelete.id; + + this.$http.post(`WorkerTimeControls/${entryId}/deleteTimeEntry`).then(() => { + this.fetchHours(); + this.vnApp.showSuccess(this.$t('Entry removed')); + }); + } + + getAbsences() { + let params = { + workerFk: this.$params.id, + started: this.started, + ended: this.ended + }; + + return this.$http.get(`WorkerCalendars/absences`, {params}) + .then(res => this.onData(res.data)); + } + + onData(data) { + const events = {}; + + let addEvent = (day, event) => { + events[new Date(day).getTime()] = event; + }; + + if (data.holidays) { + data.holidays.forEach(holiday => { + const holidayDetail = holiday.detail && holiday.detail.description; + const holidayType = holiday.type && holiday.type.name; + const holidayName = holidayDetail || holidayType; + + addEvent(holiday.dated, { + name: holidayName, + color: '#ff0' + }); + }); + } + if (data.absences) { + data.absences.forEach(absence => { + const type = absence.absenceType; + addEvent(absence.dated, { + name: type.name, + color: type.rgb + }); + }); + } + + this.weekDays.forEach(day => { + const timestamp = day.dated.getTime(); + if (events[timestamp]) + day.event = events[timestamp]; + }); + } } -Controller.$inject = ['$scope', '$http', '$stateParams', '$element', 'vnWeekDays']; +Controller.$inject = ['$element', '$scope', 'vnWeekDays']; ngModule.component('vnWorkerTimeControl', { template: require('./index.html'), diff --git a/modules/worker/front/time-control/index.spec.js b/modules/worker/front/time-control/index.spec.js index af57017ba..abfe95580 100644 --- a/modules/worker/front/time-control/index.spec.js +++ b/modules/worker/front/time-control/index.spec.js @@ -1,111 +1,115 @@ import './index.js'; -describe('Worker', () => { - describe('Component vnWorkerTimeControl', () => { - let $httpBackend; - let $scope; - let $element; - let controller; - beforeEach(ngModule('worker')); +describe('Component vnWorkerTimeControl', () => { + let $httpBackend; + let $scope; + let $element; + let controller; - beforeEach(angular.mock.inject(($compile, $rootScope, $stateParams, _$httpBackend_) => { - $stateParams.id = 1; - $httpBackend = _$httpBackend_; - $scope = $rootScope.$new(); - $element = $compile('')($scope); - controller = $element.controller('vnWorkerTimeControl'); - })); + beforeEach(ngModule('worker')); - afterEach(() => { - $scope.$destroy(); - $element.remove(); + beforeEach(angular.mock.inject(($componentController, $compile, $rootScope, $stateParams, _$httpBackend_) => { + $stateParams.id = 1; + $httpBackend = _$httpBackend_; + $scope = $rootScope.$new(); + $element = angular.element(''); + controller = $componentController('vnWorkerTimeControl', {$element, $scope}); + })); + + describe('date() setter', () => { + it(`should set the weekDays, the date in the controller and call fetchHours`, () => { + let today = new Date(); + spyOn(controller, 'fetchHours'); + + controller.date = today; + + expect(controller._date).toEqual(today); + expect(controller.started).toBeDefined(); + expect(controller.ended).toBeDefined(); + expect(controller.weekDays.length).toEqual(7); + expect(controller.fetchHours).toHaveBeenCalledWith(); }); + }); - describe('hours() setter', () => { - it(`should set hours data at it's corresponding week day`, () => { - let wednesday = new Date(controller.started.getTime()); - wednesday.setDate(wednesday.getDate() + 2); + describe('hours() setter', () => { + it(`should set hours data at it's corresponding week day`, () => { + let today = new Date(); + spyOn(controller, 'fetchHours'); - $httpBackend.whenRoute('GET', 'WorkerTimeControls/filter') - .respond([ - { - id: 1, - timed: controller.started.toJSON(), - userFk: 1 - }, { - id: 2, - timed: wednesday.toJSON(), - userFk: 1 - }, { - id: 3, - timed: wednesday.toJSON(), - userFk: 1 - } - ]); + controller.date = today; - $httpBackend.flush(); + let hours = [ + { + id: 1, + timed: controller.started.toJSON(), + userFk: 1 + }, { + id: 2, + timed: controller.ended.toJSON(), + userFk: 1 + }, { + id: 3, + timed: controller.ended.toJSON(), + userFk: 1 + } + ]; - expect(controller.weekDays.length).toEqual(7); - expect(controller.weekDays[0].hours.length).toEqual(1); - expect(controller.weekDays[2].hours.length).toEqual(2); - }); + controller.hours = hours; + + expect(controller.weekDays.length).toEqual(7); + expect(controller.weekDays[0].hours.length).toEqual(1); + expect(controller.weekDays[6].hours.length).toEqual(2); }); + }); - describe('getWeekdayTotalHours() ', () => { - it(`should return a total worked hours from 07:00 to 15:00`, () => { - const hourOne = new Date(); - hourOne.setHours(7, 0, 0, 0); - const hourTwo = new Date(); - hourTwo.setHours(10, 0, 0, 0); - const hourThree = new Date(); - hourThree.setHours(10, 20, 0, 0); - const hourFour = new Date(); - hourFour.setHours(15, 0, 0, 0); + describe('getWorkedHours() ', () => { + it(`should `, () => { + let today = new Date(); + spyOn(controller, 'fetchHours'); - const weekday = {hours: [ - {id: 1, timed: hourOne}, - {id: 2, timed: hourTwo}, - {id: 3, timed: hourThree}, - {id: 4, timed: hourFour} - ]}; + controller.date = today; - const result = controller.getWeekdayTotalHours(weekday); + let sixHoursInSeconds = 6 * 60 * 60; + let tenHoursInSeconds = 10 * 60 * 60; + let response = [ + { + dated: today, + expectedHours: sixHoursInSeconds, + workedHours: tenHoursInSeconds, - expect(result).toEqual('08:00'); - }); - }); + }, + ]; + $httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours') + .respond(response); - describe('weekTotalHours() ', () => { - it(`should return a total worked hours from a week`, () => { - const hourOne = new Date(); - hourOne.setHours(7, 0, 0, 0); - const hourTwo = new Date(); - hourTwo.setHours(10, 0, 0, 0); - const hourThree = new Date(); - hourThree.setHours(10, 20, 0, 0); - const hourFour = new Date(); - hourFour.setHours(15, 0, 0, 0); + today.setHours(0, 0, 0, 0); - const weekday = {hours: [ - {id: 1, timed: hourOne}, - {id: 2, timed: hourTwo}, - {id: 3, timed: hourThree}, - {id: 4, timed: hourFour} - ]}; - controller.weekDays = [weekday]; + let weekOffset = today.getDay() - 1; + if (weekOffset < 0) weekOffset = 6; - const weekdayHours = controller.getWeekdayTotalHours(weekday); - const weekHours = controller.weekTotalHours; + let started = new Date(today.getTime()); + started.setDate(started.getDate() - weekOffset); + controller.started = started; - expect(weekdayHours).toEqual('08:00'); - expect(weekHours).toEqual('08:00'); - }); + let ended = new Date(started.getTime()); + ended.setHours(23, 59, 59, 59); + ended.setDate(ended.getDate() + 6); + controller.ended = ended; + + controller.getWorkedHours(controller.started, controller.ended); + + $httpBackend.flush(); + + expect(controller.weekDays.length).toEqual(7); + expect(controller.weekDays[weekOffset].expectedHours).toEqual(response[0].expectedHours); + expect(controller.weekDays[weekOffset].workedHours).toEqual(response[0].workedHours); + expect(controller.weekTotalHours).toEqual('10:00'); }); describe('formatHours() ', () => { it(`should format a passed timestamp to hours and minutes`, () => { - const result = controller.formatHours(3600000); + const result = controller.formatHours(3600); expect(result).toEqual('01:00'); }); diff --git a/modules/worker/front/time-control/locale/es.yml b/modules/worker/front/time-control/locale/es.yml index 9a3484fc6..2f92657dd 100644 --- a/modules/worker/front/time-control/locale/es.yml +++ b/modules/worker/front/time-control/locale/es.yml @@ -4,4 +4,8 @@ Hour: Hora Hours: Horas Add time: Añadir hora Week total: Total semana -Current week: Semana actual \ No newline at end of file +Current week: Semana actual +This time entry will be deleted: Se borrará la hora fichada +Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla? +Finish at: Termina a las +Entry removed: Fichada borrada \ No newline at end of file diff --git a/modules/worker/front/time-control/style.scss b/modules/worker/front/time-control/style.scss index 978a600a2..5d82c6d31 100644 --- a/modules/worker/front/time-control/style.scss +++ b/modules/worker/front/time-control/style.scss @@ -1,11 +1,12 @@ @import "variables"; vn-worker-time-control { - vn-thead > vn-tr > vn-td > div { + vn-thead > vn-tr > vn-td > div.weekday { margin-bottom: 5px; color: $color-main } vn-td.hours { + min-width: 100px; vertical-align: top; & > section { diff --git a/print/common/css/email.css b/print/common/css/email.css index a4489f6d2..6e6350ff5 100644 --- a/print/common/css/email.css +++ b/print/common/css/email.css @@ -3,13 +3,17 @@ * */ body { - background-color: #EEE; + -webkit-text-size-adjust: none; + -ms-text-size-adjust: none; + background-color: #FFF; + font-weight: 400; color: #555; margin: 0 } .grid { - background-color: #EEE + background-color: #FFF + } .grid a { @@ -17,14 +21,13 @@ body { } .grid-block { - min-width: 320px; + min-width: 300px; max-width: 600px; margin: 0 auto; - width: 600px + color: #333 } h1 { font-weight: 100; - font-size: 1.5em; - color: #333; + font-size: 1.5em } diff --git a/print/common/css/layout.css b/print/common/css/layout.css index dc0c9b7b4..8a3e79219 100644 --- a/print/common/css/layout.css +++ b/print/common/css/layout.css @@ -4,8 +4,8 @@ */ .grid { - font-family: "Roboto", "Helvetica", "Arial", sans-serif; - font-size: 16px; + font-family: Helvetica, Arial, sans-serif; + font-size: 16px !important; width: 100% } @@ -15,6 +15,7 @@ .grid-block { box-sizing: border-box; + background-color: #FFF; min-height: 40px } @@ -22,10 +23,6 @@ height: 40px } -.grid-block.white { - background-color: #FFF -} - .columns { overflow: hidden; box-sizing: border-box; diff --git a/print/config/print.json b/print/config/print.json index a075feed7..e1a93e152 100755 --- a/print/config/print.json +++ b/print/config/print.json @@ -2,13 +2,14 @@ "app": { "host": "http://localhost:5000", "port": 3000, - "senderMail": "nocontestar@verdnatura.es", + "senderEmail": "nocontestar@verdnatura.es", + "reportEmail": "cau@verdnatura.es", "senderName": "Verdnatura" }, "i18n": { "locale": "es", "fallbackLocale": "es", - "silentTranslationWarn": true + "silentTranslationWarn": false }, "pdf": { "format": "A4", diff --git a/print/core/components/email-footer/assets/css/style.css b/print/core/components/email-footer/assets/css/style.css index 6e6dc755b..4bc22fdfd 100644 --- a/print/core/components/email-footer/assets/css/style.css +++ b/print/core/components/email-footer/assets/css/style.css @@ -1,4 +1,5 @@ .buttons { + font-size: 14px !important; width: 100% } diff --git a/print/core/components/email-footer/attachments.json b/print/core/components/email-footer/attachments.json index 4f3bee667..0d339b76f 100644 --- a/print/core/components/email-footer/attachments.json +++ b/print/core/components/email-footer/attachments.json @@ -8,17 +8,7 @@ "filename": "twitter.png", "path": "/assets/images/twitter.png", "cid": "twitter.png" - }, - { - "filename": "youtube.png", - "path": "/assets/images/youtube.png", - "cid": "youtube.png" - }, - { - "filename": "pinterest.png", - "path": "/assets/images/pinterest.png", - "cid": "pinterest.png" - }, + }, { "filename": "instagram.png", "path": "/assets/images/instagram.png", diff --git a/print/core/components/email-footer/email-footer.html b/print/core/components/email-footer/email-footer.html index 5b2652cb1..b60125c5d 100644 --- a/print/core/components/email-footer/email-footer.html +++ b/print/core/components/email-footer/email-footer.html @@ -29,12 +29,6 @@ Twitter - - Youtube - - - Pinterest - Instagram diff --git a/print/core/components/email-footer/locale/fr.yml b/print/core/components/email-footer/locale/fr.yml index de4cf9095..c3eb5c3ff 100644 --- a/print/core/components/email-footer/locale/fr.yml +++ b/print/core/components/email-footer/locale/fr.yml @@ -4,13 +4,13 @@ buttons: privacy: fiscalAddress: VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla · www.verdnatura.es · clientes@verdnatura.es - disclaimer: '- AVIS - Ce message est privé et confidentiel et doit être utilisé.exclusivamente - por la persona destinataria del mismo. Si has recibido este mensajepor error, - te rogamos lo comuniques al remitente y borres dicho mensaje y cualquier documentoadjunto - que pudiera contener. Verdnatura Levante SL no renuncia a la confidencialidad - ni aningún privilegio por causa de transmisión errónea o mal funcionamiento. Igualmente - no se haceresponsable de los cambios, alteraciones, errores u omisiones que pudieran - hacerse al mensaje una vez enviado.' + disclaimer: "- AVIS - Ce message est privé et confidentiel et doit être utilisé + exclusivement par le destinataire. Si vous avez reçu ce message par erreur, + veuillez en informer l'expéditeur et supprimer ce message ainsi que tous les + documents joints qu'il pourrait contenir. Verdnatura Levante SL ne renonce pas à la + confidentialité ni aux privilèges résultant d'une transmission erronée ou d'un dysfonctionnement. + De même, il n'est pas responsable des modifications, altérations, erreurs ou + omissions qui pourraient être apportées au message une fois envoyé." law: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de Datos de Carácter Personal, te comunicamos que los datos personales que facilites se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L.,pudiendo en diff --git a/print/core/components/email-header/assets/css/style.css b/print/core/components/email-header/assets/css/style.css index 5d2f658ce..4db5e2b2e 100644 --- a/print/core/components/email-header/assets/css/style.css +++ b/print/core/components/email-header/assets/css/style.css @@ -8,7 +8,7 @@ header .logo img { header .topbar { background-color: #95d831; - height: 25px + height: 10px } .topbar:after { diff --git a/print/core/components/report-footer/report-footer.html b/print/core/components/report-footer/report-footer.html index af433676f..a87c36109 100644 --- a/print/core/components/report-footer/report-footer.html +++ b/print/core/components/report-footer/report-footer.html @@ -4,6 +4,6 @@
        {{centerText}}
        {{$t('numPages')}}
        -

        {{$t('law.phytosanitary')}}

        +

        {{$t('law.phytosanitary')}}

        diff --git a/print/core/components/report-footer/report-footer.js b/print/core/components/report-footer/report-footer.js index 10f411316..df1dca665 100755 --- a/print/core/components/report-footer/report-footer.js +++ b/print/core/components/report-footer/report-footer.js @@ -1,4 +1,4 @@ module.exports = { name: 'report-footer', - props: ['leftText', 'centerText', 'locale'] + props: ['leftText', 'centerText', 'locale', 'showPhytosanitary'] }; diff --git a/print/core/config.js b/print/core/config.js index d07d4f3fc..8db388401 100644 --- a/print/core/config.js +++ b/print/core/config.js @@ -10,8 +10,11 @@ let configFiles = [ ]; for (let configFile of configFiles) { - if (fs.existsSync(configFile)) - Object.assign(config, require(configFile)); + if (fs.existsSync(configFile)) { + const conf = require(configFile); + for (let prop in conf) + Object.assign(config[prop], conf[prop]); + } } config.env = env; diff --git a/print/core/database.js b/print/core/database.js index 02ae2a9b0..553fe3265 100644 --- a/print/core/database.js +++ b/print/core/database.js @@ -10,13 +10,20 @@ module.exports = { }); } }, - find(query, params) { + /** + * Makes a query from a raw sql + * @param {String} query - The raw SQL query + * @param {Object} params - Parameterized values + * + * @return {Object} - Result + */ + rawSql(query, params) { return this.pool.query(query, params).then(([rows]) => { return rows; }); }, findOne(query, params) { - return this.find(query, params).then(([rows]) => rows); + return this.rawSql(query, params).then(([rows]) => rows); }, findFromDef() { diff --git a/print/core/mixins/prop-validator.js b/print/core/mixins/prop-validator.js index d380f723d..a73197ebf 100644 --- a/print/core/mixins/prop-validator.js +++ b/print/core/mixins/prop-validator.js @@ -16,7 +16,7 @@ const validator = { if (invalidProps.length > 0) { const required = invalidProps.join(', '); - throw new Error(`Required params not found [${required}]`); + throw new Error(`Required properties not found [${required}]`); } }, props: ['isPreview'] diff --git a/print/core/router.js b/print/core/router.js index 173c31565..f015ac03b 100644 --- a/print/core/router.js +++ b/print/core/router.js @@ -1,51 +1,6 @@ -const Report = require('./report'); -const Email = require('./email'); - module.exports = app => { - app.get(`/api/report/:name`, async(req, res, next) => { - const args = req.query; - const requiredArgs = ['clientId']; - - const hasRequiredArgs = requiredArgs.every(arg => { - return args[arg]; - }); - - if (!hasRequiredArgs) - res.json({message: 'Required params recipient, clientId'}); - - try { - const report = new Report(req.params.name, args); - const stream = await report.toPdfStream(); - res.setHeader('Content-type', 'application/pdf'); - stream.pipe(res); - } catch (e) { - next(e); - } - }); - - app.get(`/api/email/:name`, async(req, res, next) => { - const args = req.query; - const requiredArgs = ['recipient', 'clientId']; - - const hasRequiredArgs = requiredArgs.every(arg => { - return args[arg]; - }); - - if (!hasRequiredArgs) - res.json({message: 'Required params recipient, clientId'}); - - try { - const email = new Email(req.params.name, args); - if (args.isPreview === 'true') { - const rendered = await email.render(); - - res.send(rendered); - } else { - await email.send(); - res.status(200).json({message: 'Sent'}); - } - } catch (e) { - next(e); - } - }); + // Import methods + require('../methods/closure')(app); + require('../methods/report')(app); + require('../methods/email')(app); }; diff --git a/print/core/smtp.js b/print/core/smtp.js index 6f8bb20f9..b274eafa3 100644 --- a/print/core/smtp.js +++ b/print/core/smtp.js @@ -1,5 +1,6 @@ const nodemailer = require('nodemailer'); const config = require('./config'); +const db = require('./database'); module.exports = { init() { @@ -8,15 +9,30 @@ module.exports = { }, send(options) { - options.from = `${config.app.senderName} <${config.app.senderMail}>`; + options.from = `${config.app.senderName} <${config.app.senderEmail}>`; if (process.env.NODE_ENV !== 'production') { if (!config.smtp.auth.user) return Promise.resolve(true); - options.to = config.app.senderMail; + options.to = config.app.senderEmail; } - return this.transporter.sendMail(options); + let error; + return this.transporter.sendMail(options).catch(err => { + error = err; + + throw err; + }).finally(async() => { + await db.rawSql(` + INSERT INTO vn.mail (sender, replyTo, sent, subject, body, status) + VALUES (:recipient, :sender, 1, :subject, :body, :status)`, { + sender: config.app.senderEmail, + recipient: options.to, + subject: options.subject, + body: options.text || options.html, + status: error && error.message || 'Sent' + }); + }); } }; diff --git a/print/methods/closure.js b/print/methods/closure.js new file mode 100644 index 000000000..c12dd07b0 --- /dev/null +++ b/print/methods/closure.js @@ -0,0 +1,59 @@ +const db = require('../core/database'); +const Email = require('../core/email'); +const smtp = require('../core/smtp'); +const config = require('../core/config'); + +module.exports = app => { + app.get('/api/closure', async function(req, res) { + const failedtickets = []; + const tickets = await db.rawSql(` + SELECT + t.id, + t.clientFk, + c.email recipient + FROM expedition e + JOIN ticket t ON t.id = e.ticketFk + JOIN client c ON c.id = t.clientFk + JOIN warehouse w ON w.id = t.warehouseFk AND hasComission + LEFT JOIN ticketState ts ON ts.ticketFk = t.id + WHERE + ts.alertLevel = 2 + AND DATE(t.shipped) BETWEEN DATE_ADD(CURDATE(), INTERVAL -2 DAY) AND CURDATE() + AND t.refFk IS NULL + GROUP BY e.ticketFk`); + + for (const ticket of tickets) { + try { + await db.rawSql(`CALL vn.ticketClosureTicket(:ticketId)`, { + ticketId: ticket.id + }); + + const args = { + ticketId: ticket.id, + clientId: ticket.clientFk, + recipient: ticket.recipient + }; + const email = new Email('delivery-note-link', args); + await email.send(); + } catch (error) { + // Save tickets on a list of failed ids + failedtickets.push(ticket.id); + } + } + + // Send email with failed tickets + if (failedtickets.length > 0) { + const ticketList = failedtickets.join(', '); + smtp.send({ + to: config.app.reportEmail, + subject: 'Nightly ticket closure has failed', + text: `This following tickets has failed: ${ticketList}` + }); + } + + res.status(200).json({ + statusCode: 200, + message: 'Closure executed successfully' + }); + }); +}; diff --git a/print/methods/email.js b/print/methods/email.js new file mode 100644 index 000000000..a56798820 --- /dev/null +++ b/print/methods/email.js @@ -0,0 +1,32 @@ +const Email = require('../core/email'); + +module.exports = app => { + app.get(`/api/email/:name`, async(req, res, next) => { + const args = req.query; + const requiredArgs = ['clientId', 'recipient']; + const argList = requiredArgs.join(','); + const hasRequiredArgs = requiredArgs.every(arg => { + return args[arg]; + }); + + try { + if (!hasRequiredArgs) + throw new Error(`Required properties not found [${argList}]`); + + const email = new Email(req.params.name, args); + if (args.isPreview === 'true') { + const rendered = await email.render(); + + res.send(rendered); + } else { + await email.send(); + + res.status(200).json({ + message: 'Sent' + }); + } + } catch (e) { + next(e); + } + }); +}; diff --git a/print/methods/report.js b/print/methods/report.js new file mode 100644 index 000000000..626830c39 --- /dev/null +++ b/print/methods/report.js @@ -0,0 +1,24 @@ +const Report = require('../core/report'); + +module.exports = app => { + app.get(`/api/report/:name`, async(req, res, next) => { + const args = req.query; + const requiredArgs = ['clientId']; + const argList = requiredArgs.join(','); + const hasRequiredArgs = requiredArgs.every(arg => { + return args[arg]; + }); + + try { + if (!hasRequiredArgs) + throw new Error(`Required properties not found [${argList}]`); + + const report = new Report(req.params.name, args); + const stream = await report.toPdfStream(); + res.setHeader('Content-type', 'application/pdf'); + stream.pipe(res); + } catch (error) { + next(error); + } + }); +}; diff --git a/print/templates/email/claim-pickup-order/attachments.json b/print/templates/email/claim-pickup-order/attachments.json index 8fb5569e4..5d7c15fb9 100644 --- a/print/templates/email/claim-pickup-order/attachments.json +++ b/print/templates/email/claim-pickup-order/attachments.json @@ -1,4 +1,6 @@ -[{ - "filename": "claim-pickup-order.pdf", - "component": "claim-pickup" -}] \ No newline at end of file +[ + { + "filename": "claim-pickup-order.pdf", + "component": "claim-pickup" + } +] \ No newline at end of file diff --git a/print/templates/email/claim-pickup-order/claim-pickup-order.html b/print/templates/email/claim-pickup-order/claim-pickup-order.html index de08d8d8c..039f5761a 100644 --- a/print/templates/email/claim-pickup-order/claim-pickup-order.html +++ b/print/templates/email/claim-pickup-order/claim-pickup-order.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{$t('description.dear')}},

        {{$t('description.instructions')}}

        diff --git a/print/templates/email/client-lcr/assets/css/index.js b/print/templates/email/client-lcr/assets/css/index.js deleted file mode 100644 index 321c632dc..000000000 --- a/print/templates/email/client-lcr/assets/css/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const CssReader = require(`${appPath}/lib/cssReader`); - -module.exports = new CssReader([ - `${appPath}/common/css/layout.css`, - `${appPath}/common/css/email.css`, - `${appPath}/common/css/misc.css`]) - .mergeStyles(); diff --git a/print/templates/email/client-lcr/index.html b/print/templates/email/client-lcr/index.html deleted file mode 100644 index 91fa67ab5..000000000 --- a/print/templates/email/client-lcr/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - {{ $t('subject') }} - - -
        - - - -
        - -
        -

        {{ $t('title') }}

        -
        - - -

        {{$t('description.dear')}},

        -

        {{$t('description.instructions')}}

        -

        {{$t('description.conclusion')}}

        -
        - - - -
        - - \ No newline at end of file diff --git a/print/templates/email/client-lcr/index.js b/print/templates/email/client-lcr/index.js deleted file mode 100755 index bb7ba452d..000000000 --- a/print/templates/email/client-lcr/index.js +++ /dev/null @@ -1,49 +0,0 @@ -const database = require(`${appPath}/lib/database`); -const reportEngine = require(`${appPath}/lib/reportEngine.js`); -const UserException = require(`${appPath}/lib/exceptions/userException`); - -module.exports = { - name: 'client-lcr', - async asyncData(ctx, params) { - const promises = []; - const data = { - isPreview: ctx.method === 'GET', - }; - - if (!params.clientFk) - throw new UserException('No client id specified'); - - promises.push(reportEngine.toPdf('rpt-lcr', ctx)); - promises.push(this.methods.fetchClient(params.clientFk)); - - return Promise.all(promises).then(result => { - const stream = result[0]; - const [[client]] = result[1]; - - Object.assign(data, client); - Object.assign(data, {attachments: [{filename: 'rpt-lcr.pdf', content: stream}]}); - - return data; - }); - }, - created() { - if (this.locale) - this.$i18n.locale = this.locale; - }, - - methods: { - fetchClient(clientFk) { - return database.pool.query(` - SELECT - u.lang locale, - c.email recipient - FROM client c - JOIN account.user u ON u.id = c.id - WHERE c.id = ?`, [clientFk]); - }, - }, - components: { - 'email-header': require('../email-header'), - 'email-footer': require('../email-footer'), - }, -}; diff --git a/print/templates/email/client-lcr/locale.js b/print/templates/email/client-lcr/locale.js deleted file mode 100644 index 85a75d778..000000000 --- a/print/templates/email/client-lcr/locale.js +++ /dev/null @@ -1,64 +0,0 @@ -module.exports = { - messages: { - es: { - subject: 'Autorisation pour débit', - title: 'Autorisation pour débit', - description: { - dear: 'Messieurs', - instructions: `Étant donné les excellentes relations existantes entre nos - deux sociétés et en vue de faciliter les processus de - paiement de nos factures, nous vous suggérons l'utilisation - du système française de compensation LCR. - Ce service consiste à effectuer des recouvrements - automatiques, de manière électronique, de nos effets - - lettres de change et billets à ordre - tirés sur votre société - en Euro, qui présente comme principal avantage pour vous - la substantielle réduction de coûts dans des termes de frais - et commissions bancaires. - Dans le cas où vous accepteriez notre proposition, à - l’échéance de chaque effet, votre compte sera débité - automatiquement par votre Banque. - Ainsi, nous vous demandons de signer et envoyer à votre - Banque l'original de l'autorisation pour débit en annexe, - dûment remplie, et de nous retourner une photocopie de la - dite autorisation. - Ce système étant basé sur la transmission de données de - manière électronique, le maniement de documents - physiques á été éliminé - En vous remercieront pour votre collaboration, nous vous - prions d’agréer, Messieurs, nos salutations distinguées.`, - conclusion: 'Bien cordialement' - }, - }, - fr: { - subject: 'Autorisation pour débit', - title: 'Autorisation pour débit', - description: { - dear: 'Messieurs', - instructions: `Étant donné les excellentes relations existantes entre nos - deux sociétés et en vue de faciliter les processus de - paiement de nos factures, nous vous suggérons l'utilisation - du système française de compensation LCR. - Ce service consiste à effectuer des recouvrements - automatiques, de manière électronique, de nos effets - - lettres de change et billets à ordre - tirés sur votre société - en Euro, qui présente comme principal avantage pour vous - la substantielle réduction de coûts dans des termes de frais - et commissions bancaires. - Dans le cas où vous accepteriez notre proposition, à - l’échéance de chaque effet, votre compte sera débité - automatiquement par votre Banque. - Ainsi, nous vous demandons de signer et envoyer à votre - Banque l'original de l'autorisation pour débit en annexe, - dûment remplie, et de nous retourner une photocopie de la - dite autorisation. - Ce système étant basé sur la transmission de données de - manière électronique, le maniement de documents - physiques á été éliminé - En vous remercieront pour votre collaboration, nous vous - prions d’agréer, Messieurs, nos salutations distinguées.`, - conclusion: 'Bien cordialement' - }, - }, - }, -}; diff --git a/print/templates/email/client-welcome/client-welcome.html b/print/templates/email/client-welcome/client-welcome.html index 88eaf56d0..dc5c5e572 100644 --- a/print/templates/email/client-welcome/client-welcome.html +++ b/print/templates/email/client-welcome/client-welcome.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{$t('dearClient')}},

        diff --git a/print/templates/email/delivery-note-link/assets/css/import.js b/print/templates/email/delivery-note-link/assets/css/import.js new file mode 100644 index 000000000..c742fdf90 --- /dev/null +++ b/print/templates/email/delivery-note-link/assets/css/import.js @@ -0,0 +1,9 @@ +const Stylesheet = require(`${appPath}/core/stylesheet`); + +module.exports = new Stylesheet([ + `${appPath}/common/css/spacing.css`, + `${appPath}/common/css/misc.css`, + `${appPath}/common/css/layout.css`, + `${appPath}/common/css/email.css`, + `${__dirname}/style.css`]) + .mergeStyles(); diff --git a/print/templates/email/delivery-note-link/assets/css/style.css b/print/templates/email/delivery-note-link/assets/css/style.css new file mode 100644 index 000000000..5db85befa --- /dev/null +++ b/print/templates/email/delivery-note-link/assets/css/style.css @@ -0,0 +1,5 @@ +.external-link { + border: 2px dashed #8dba25; + border-radius: 3px; + text-align: center +} \ No newline at end of file diff --git a/print/templates/email/delivery-note-link/delivery-note-link.html b/print/templates/email/delivery-note-link/delivery-note-link.html new file mode 100644 index 000000000..2be79a118 --- /dev/null +++ b/print/templates/email/delivery-note-link/delivery-note-link.html @@ -0,0 +1,75 @@ + + + + + + {{ $t('subject') }} + + + + + + + + + +
        + +
        +
        +
        + +
        +
        + + +
        +
        + +
        +
        +

        {{ $t('title') }}

        +

        {{$t('dear')}}

        +

        +
        +
        + +
        +
        +

        {{$t('copyLink')}}

        + +
        +
        + +
        +
        +

        +

        +

        +
        +
        + + +
        +
        + + +
        +
        + +
        +
        +
        +
        + + \ No newline at end of file diff --git a/print/templates/email/delivery-note-link/delivery-note-link.js b/print/templates/email/delivery-note-link/delivery-note-link.js new file mode 100755 index 000000000..009fe7b5b --- /dev/null +++ b/print/templates/email/delivery-note-link/delivery-note-link.js @@ -0,0 +1,16 @@ +const Component = require(`${appPath}/core/component`); +const emailHeader = new Component('email-header'); +const emailFooter = new Component('email-footer'); + +module.exports = { + name: 'delivery-note-link', + components: { + 'email-header': emailHeader.build(), + 'email-footer': emailFooter.build() + }, + props: { + ticketId: { + required: true + } + } +}; diff --git a/print/templates/email/delivery-note-link/locale/es.yml b/print/templates/email/delivery-note-link/locale/es.yml new file mode 100644 index 000000000..47c7f11da --- /dev/null +++ b/print/templates/email/delivery-note-link/locale/es.yml @@ -0,0 +1,11 @@ +subject: Aquí tienes tu albarán +title: "Aquí tienes tu albarán" +dear: Estimado cliente +description: Ya está disponible el albarán correspondiente al pedido {0}.
        + Puedes verlo haciendo clic en este enlace. +copyLink: 'Como alternativa, puedes copiar el siguiente enlace en tu navegador:' +poll: Si lo deseas, puedes responder a nuestra encuesta de satisfacción para + ayudarnos a prestar un mejor servicio. ¡Tu opinión es muy importante para nosotros! +help: Cualquier duda que te surja, no dudes en consultarla, ¡estamos para + atenderte! +conclusion: ¡Gracias por tu atención! diff --git a/print/templates/email/delivery-note-link/locale/fr.yml b/print/templates/email/delivery-note-link/locale/fr.yml new file mode 100644 index 000000000..3ecf357e1 --- /dev/null +++ b/print/templates/email/delivery-note-link/locale/fr.yml @@ -0,0 +1,10 @@ +subject: Voici votre bon de livraison +title: "Voici votre bon de livraison" +dear: Cher client, +description: Le bon de livraison correspondant à la commande {0} est maintenant disponible.
        + Vous pouvez le voir en cliquant sur ce lien. +copyLink: 'Vous pouvez également copier le lien suivant dans votre navigateur:' +poll: Si vous le souhaitez, vous pouvez répondre à notre questionaire de satisfaction + pour nous aider à améliorer notre service. Votre avis est très important pour nous! +help: N'hésitez pas nous envoyer toute doute ou question, nous sommes là pour vous aider! +conclusion: Merci pour votre attention! diff --git a/print/templates/email/delivery-note/attachments.json b/print/templates/email/delivery-note/attachments.json index d963db0c5..19519a387 100644 --- a/print/templates/email/delivery-note/attachments.json +++ b/print/templates/email/delivery-note/attachments.json @@ -1,4 +1,6 @@ -[{ - "filename": "delivery-note.pdf", - "component": "delivery-note" -}] \ No newline at end of file +[ + { + "filename": "delivery-note.pdf", + "component": "delivery-note" + } +] \ No newline at end of file diff --git a/print/templates/email/delivery-note/delivery-note.html b/print/templates/email/delivery-note/delivery-note.html index ff6186eb2..dcc7f0d56 100644 --- a/print/templates/email/delivery-note/delivery-note.html +++ b/print/templates/email/delivery-note/delivery-note.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,11 +25,13 @@
        -
        +

        {{ $t('title') }}

        -

        {{$t('dearClient')}},

        -

        +

        {{$t('dear')}},

        +

        +

        +

        diff --git a/print/templates/email/delivery-note/locale/es.yml b/print/templates/email/delivery-note/locale/es.yml index cdc63e8eb..3294b2316 100644 --- a/print/templates/email/delivery-note/locale/es.yml +++ b/print/templates/email/delivery-note/locale/es.yml @@ -1,6 +1,10 @@ subject: Aquí tienes tu albarán title: "¡Este es tu albarán!" -dearClient: Estimado cliente -clientData: A continuación adjuntamos tu albarán. +dear: Estimado cliente +description: Ya está disponible el albarán correspondiente al pedido {0}.
        + Puedes descargarlo haciendo clic en el adjunto de este correo. +poll: Si lo deseas, puedes responder a nuestra encuesta de satisfacción para + ayudarnos a prestar un mejor servicio. ¡Tu opinión es muy importante para nosotros! help: Cualquier duda que te surja, no dudes en consultarla, ¡estamos para - atenderte! \ No newline at end of file + atenderte! +conclusion: ¡Gracias por tu atención! diff --git a/print/templates/email/delivery-note/locale/fr.yml b/print/templates/email/delivery-note/locale/fr.yml new file mode 100644 index 000000000..fdaf6e320 --- /dev/null +++ b/print/templates/email/delivery-note/locale/fr.yml @@ -0,0 +1,9 @@ +subject: Voici votre bon de livraison +title: "Voici votre bon de livraison!" +dear: Cher client, +description: Le bon de livraison correspondant à la commande {0} est maintenant disponible.
        + Vous pouvez le télécharger en cliquant sur la pièce jointe dans cet email. +poll: Si vous le souhaitez, vous pouvez répondre à notre questionaire de satisfaction + pour nous aider à améliorer notre service. Votre avis est très important pour nous! +help: N'hésitez pas nous envoyer toute doute ou question, nous sommes là pour vous aider! +conclusion: Merci pour votre attention! diff --git a/print/templates/email/driver-route/attachments.json b/print/templates/email/driver-route/attachments.json index e80a74ce0..01111c464 100644 --- a/print/templates/email/driver-route/attachments.json +++ b/print/templates/email/driver-route/attachments.json @@ -1,4 +1,6 @@ -[{ - "filename": "driver-route.pdf", - "component": "driver-route" -}] \ No newline at end of file +[ + { + "filename": "driver-route.pdf", + "component": "driver-route" + } +] \ No newline at end of file diff --git a/print/templates/email/driver-route/driver-route.html b/print/templates/email/driver-route/driver-route.html index 33fcebd22..40635bab1 100644 --- a/print/templates/email/driver-route/driver-route.html +++ b/print/templates/email/driver-route/driver-route.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{$t('description.instructions')}}

        diff --git a/print/templates/email/letter-debtor-nd/attachments.json b/print/templates/email/letter-debtor-nd/attachments.json index ae8a3f4cc..1e21ea343 100644 --- a/print/templates/email/letter-debtor-nd/attachments.json +++ b/print/templates/email/letter-debtor-nd/attachments.json @@ -1,4 +1,6 @@ -[{ - "filename": "letter-debtor.pdf", - "component": "letter-debtor" -}] \ No newline at end of file +[ + { + "filename": "letter-debtor.pdf", + "component": "letter-debtor" + } +] \ No newline at end of file diff --git a/print/templates/email/letter-debtor-nd/letter-debtor-nd.html b/print/templates/email/letter-debtor-nd/letter-debtor-nd.html index 7a2ae877a..5299ab5be 100644 --- a/print/templates/email/letter-debtor-nd/letter-debtor-nd.html +++ b/print/templates/email/letter-debtor-nd/letter-debtor-nd.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{ $t('sections.introduction.title') }},

        {{ $t('sections.introduction.description') }}

        @@ -63,7 +65,7 @@
        -
        +
        + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{ $t('sections.introduction.title') }},

        {{ $t('sections.introduction.description') }}

        @@ -46,7 +48,7 @@
        -
        +
        + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{ $t('sections.introduction.title') }},

        diff --git a/print/templates/email/printer-setup/attachments.json b/print/templates/email/printer-setup/attachments.json index 2f828a6a0..1e1f710c3 100644 --- a/print/templates/email/printer-setup/attachments.json +++ b/print/templates/email/printer-setup/attachments.json @@ -1,12 +1,14 @@ -[{ - "filename": "model.ezp", - "component": "printer-setup", - "path": "/assets/files/model.ezp", - "cid": "model.ezp" -}, -{ - "filename": "port.png", - "component": "printer-setup", - "path": "/assets/files/port.png", - "cid": "port.png" -}] \ No newline at end of file +[ + { + "filename": "model.ezp", + "component": "printer-setup", + "path": "/assets/files/model.ezp", + "cid": "model.ezp" + }, + { + "filename": "port.png", + "component": "printer-setup", + "path": "/assets/files/port.png", + "cid": "port.png" + } +] \ No newline at end of file diff --git a/print/templates/email/printer-setup/printer-setup.html b/print/templates/email/printer-setup/printer-setup.html index 08ef588d4..e6b66cd98 100644 --- a/print/templates/email/printer-setup/printer-setup.html +++ b/print/templates/email/printer-setup/printer-setup.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,7 +25,7 @@
        -
        +

        {{ $t('title') }}

        {{$t('description.dear')}},

        {{$t('description.instructions')}}

        @@ -41,7 +43,7 @@
        -
        +

        {{$t('sections.help.title')}}

        {{$t('sections.help.description')}}

        @@ -49,7 +51,7 @@
        -
        +
        {{$t('salesPersonName')}}: {{client.salesPersonName}}
        @@ -64,7 +66,7 @@
        -
        +
        Dadas las excelentes relaciones existentes entre nuestras + dos empresas y para facilitar los procesos de pago de nuestras facturas, + sugerimos el uso del sistema de domiciliación bancaria SEPA CORE.

        +

        Este servicio consiste en emitir nuestros recibos a su empresa de + forma automatizada y electrónicamente, lo que supone para usted una reducción + sustancial de costos en términos de honorarios y gastos bancarios.

        +

        En caso de que acepte nuestra propuesta, a la fecha de vencimiento de cada efecto, + se debitará a su cuenta automáticamente a través de su entidad bancaria. + Por tanto, le pedimos que firme y envíe a su banco la autorización original adjunta, + debidamente cumplimentada, y nos devuelva una fotocopia de dicha autorización.

        +

        Este sistema se basa en la transmisión electrónica de datos; + el manejo de documentos físicos ha sido eliminado.

        +

        Le agradecemos su cooperación,

        + conclusion: ¡Gracias por su atención! diff --git a/print/templates/email/sepa-core/locale/fr.yml b/print/templates/email/sepa-core/locale/fr.yml new file mode 100644 index 000000000..98bd7593a --- /dev/null +++ b/print/templates/email/sepa-core/locale/fr.yml @@ -0,0 +1,27 @@ +subject: Autorisation pour débit +title: Autorisation pour débit +description: + dear: Messieurs + instructions:

        Étant donné les excellentes relations existantes entre nos + deux sociétés et en vue de faciliter les processus de + paiement de nos factures, nous vous suggérons l'utilisation + du système française de compensation LCR.

        +

        Ce service consiste à effectuer des recouvrements + automatiques, de manière électronique, de nos effets - + lettres de change et billets à ordre - tirés sur votre société + en Euro, qui présente comme principal avantage pour vous + la substantielle réduction de coûts dans des termes de frais + et commissions bancaires.

        +

        Dans le cas où vous accepteriez notre proposition, à + l’échéance de chaque effet, votre compte sera débité + automatiquement par votre Banque. + Ainsi, nous vous demandons de signer et envoyer à votre + Banque l'original de l'autorisation pour débit en annexe, + dûment remplie, et de nous retourner une photocopie de la + dite autorisation.

        +

        Ce système étant basé sur la transmission de données de + manière électronique, le maniement de documents + physiques á été éliminé

        +

        En vous remercieront pour votre collaboration, nous vous + prions d’agréer, Messieurs, nos salutations distinguées.

        + conclusion: Bien cordialement diff --git a/print/templates/email/sepa-core/sepa-core.html b/print/templates/email/sepa-core/sepa-core.html index 1708099a9..0fc09cc9c 100644 --- a/print/templates/email/sepa-core/sepa-core.html +++ b/print/templates/email/sepa-core/sepa-core.html @@ -1,6 +1,8 @@ + + {{ $t('subject') }} @@ -23,16 +25,16 @@
        -
        +

        {{ $t('title') }}

        {{$t('description.dear')}},

        -

        {{$t('description.instructions')}}

        +

        {{$t('description.conclusion')}}

        - +
        -
        +
        {{$t('date')}} - {{currentDate}} + {{dated}} diff --git a/print/templates/reports/claim-pickup-order/claim-pickup-order.js b/print/templates/reports/claim-pickup-order/claim-pickup-order.js index 494c8b4e5..a18e6d0e0 100755 --- a/print/templates/reports/claim-pickup-order/claim-pickup-order.js +++ b/print/templates/reports/claim-pickup-order/claim-pickup-order.js @@ -13,7 +13,7 @@ module.exports = { throw new Error('Something went wrong'); }, computed: { - currentDate: function() { + dated: function() { const filters = this.$options.filters; return filters.date(new Date(), '%d-%m-%Y'); @@ -43,7 +43,7 @@ module.exports = { WHERE cl.id = ?`, [claimId]); }, fetchSales(claimId) { - return db.find( + return db.rawSql( `SELECT s.id, s.quantity, diff --git a/print/templates/reports/delivery-note/delivery-note.html b/print/templates/reports/delivery-note/delivery-note.html index 65d1a04bc..3908133cc 100644 --- a/print/templates/reports/delivery-note/delivery-note.html +++ b/print/templates/reports/delivery-note/delivery-note.html @@ -238,6 +238,7 @@
        { + return db.rawSql(`CALL vn.ticketGetTaxAdd(?)`, [ticketId]).then(rows => { return rows[0]; }); }, fetchPackagings(ticketId) { - return db.find( + return db.rawSql( `SELECT tp.quantity, i.name, @@ -145,7 +145,7 @@ module.exports = { ORDER BY itemFk`, [ticketId]); }, fetchServices(ticketId) { - return db.find( + return db.rawSql( `SELECT tc.description taxDescription, ts.description, diff --git a/print/templates/reports/driver-route/driver-route.js b/print/templates/reports/driver-route/driver-route.js index 845beaa9a..908f49370 100755 --- a/print/templates/reports/driver-route/driver-route.js +++ b/print/templates/reports/driver-route/driver-route.js @@ -12,13 +12,6 @@ module.exports = { if (!this.route) throw new Error('Something went wrong'); }, - computed: { - dated: function() { - const filters = this.$options.filters; - - return filters.date(new Date(), '%d-%m-%Y'); - } - }, methods: { fetchRoute(id) { return db.findOne( @@ -41,7 +34,7 @@ module.exports = { }, // Redmine #1855 Replace function Averiguar_ComercialCliente_Id() fetchTickets(routeId) { - return db.find( + return db.rawSql( `SELECT t.nickname addressName, t.packages, diff --git a/print/templates/reports/item-label/item-label.js b/print/templates/reports/item-label/item-label.js index 026d0fc52..60139bbb2 100755 --- a/print/templates/reports/item-label/item-label.js +++ b/print/templates/reports/item-label/item-label.js @@ -44,7 +44,7 @@ module.exports = { WHERE i.id = ? AND clb.warehouse_id = ?`, [id, warehouseId]); }, fetchItemTags(itemId) { - return db.find( + return db.rawSql( `SELECT t.code, t.name, it.value FROM vn.itemTag it JOIN vn.tag t ON t.id = it.tagFk diff --git a/print/templates/reports/letter-debtor/letter-debtor.js b/print/templates/reports/letter-debtor/letter-debtor.js index b2983eb51..f099c1869 100755 --- a/print/templates/reports/letter-debtor/letter-debtor.js +++ b/print/templates/reports/letter-debtor/letter-debtor.js @@ -40,7 +40,7 @@ module.exports = { WHERE c.id = ?`, [clientId]); }, fetchSales(clientId, companyId) { - return db.find( + return db.rawSql( `CALL vn.clientGetDebtDiary(:clientId, :companyId)`, { clientId: clientId, companyId: companyId, diff --git a/print/templates/reports/rpt-lcr/assets/css/index.js b/print/templates/reports/rpt-lcr/assets/css/index.js deleted file mode 100644 index 515dea750..000000000 --- a/print/templates/reports/rpt-lcr/assets/css/index.js +++ /dev/null @@ -1,8 +0,0 @@ -const CssReader = require(`${appPath}/lib/cssReader`); - -module.exports = new CssReader([ - `${appPath}/common/css/layout.css`, - `${appPath}/common/css/report.css`, - `${appPath}/common/css/misc.css`, - `${__dirname}/style.css`]) - .mergeStyles(); diff --git a/print/templates/reports/rpt-lcr/assets/css/style.css b/print/templates/reports/rpt-lcr/assets/css/style.css deleted file mode 100644 index 94b93e153..000000000 --- a/print/templates/reports/rpt-lcr/assets/css/style.css +++ /dev/null @@ -1,31 +0,0 @@ -.payment-type { - width: auto -} - -.payment-type th:nth-child(2), .payment-type th:nth-child(5) { - padding: 10px !important -} - -.payment-type th:nth-child(3){ - padding: 0 50px !important -} - -.table-margin { - margin-top: 20px -} - -.grey-background { - background-color: #DDD -} - -.emptyField { - width: 100%; -} - -.row-oriented.input-table > tbody > tr > td { - width: 10% !important -} - -.row-oriented.input-table > tbody > tr > th { - width: 90% !important -} \ No newline at end of file diff --git a/print/templates/reports/rpt-lcr/assets/images/signature.png b/print/templates/reports/rpt-lcr/assets/images/signature.png deleted file mode 100644 index c69cc4798..000000000 Binary files a/print/templates/reports/rpt-lcr/assets/images/signature.png and /dev/null differ diff --git a/print/templates/reports/rpt-lcr/index.html b/print/templates/reports/rpt-lcr/index.html deleted file mode 100644 index 236ace8ed..000000000 --- a/print/templates/reports/rpt-lcr/index.html +++ /dev/null @@ -1,189 +0,0 @@ - - - -
        - - - -
        -

        {{$t('title')}}

        -
        -
        {{$t('Creditor')}}
        -
        - - - - - - - - - - - - - - - - - - - -
        {{$t('supplier.name')}}:{{supplierName}}
        {{$t('supplier.street')}}:{{supplierStreet}}
        {{supplierPostCode}}, {{supplierCity}} ({{supplierProvince}})
        {{supplierCountry}}
        -
        -
        -
        -
        {{$t('Deptor')}}
        -
        - - - - - - - - - - - - - - - - - - - - - - - -
        {{$t('client.name')}}:{{clientName}}
        {{$t('client.street')}}:{{clientStreet}}
        {{clientPostCode}}, {{clientCity}} ({{clientProvince}})
        {{clientCountry}}
        {{$t('client.fi')}}: -
        - {{fi.charAt(i)}} -
        -
        -
        -
        -

        {{$t('description')}}

        -
        -
        {{$t('Bank')}}
        -
        -
        - {{$t('client.toCompleteByClient')}} -
        - - - - - - - - - - - -
        {{$t('bank.name')}}:
        {{$t('bank.street')}}:
        - - - - - - - - - - - - - - - - - - - - -
        {{$t('bank.account')}}:
        -
        - -
        -
        -
        - -
        -
        -
        - -
        -
        -
        - -
        -
        -
        -
        - {{$t('bank.bankCode')}} -
        -
        -
        -
        -
        - {{$t('bank.agencyCode')}} -
        -
        -
        -
        -
        - {{$t('bank.accountNumber')}} -
        -
        -
        -
        -
        - {{$t('bank.ribKey')}} -
        -
        -
        - - - - - - - - - - - -
        IBAN:
        -
        - F - R - -
        -
        -
        -
        -

        {{$t('authorization')}}

        - - -
        -
        {{$t('client.sign')}}
        -
        -
        -

        {{$t('client.signDate')}}:

        -
        -
        -
        -
        - - - - -
        - - \ No newline at end of file diff --git a/print/templates/reports/rpt-lcr/index.js b/print/templates/reports/rpt-lcr/index.js deleted file mode 100755 index d4342764c..000000000 --- a/print/templates/reports/rpt-lcr/index.js +++ /dev/null @@ -1,80 +0,0 @@ -const strftime = require('strftime'); -const database = require(`${appPath}/lib/database`); -const UserException = require(`${appPath}/lib/exceptions/userException`); - -module.exports = { - name: 'rpt-lcr', - async asyncData(ctx, params) { - if (!params.clientFk) - throw new UserException('No client id specified'); - - if (!params.companyFk) - throw new UserException('No company id specified'); - - return this.methods.fetchClient(params.clientFk, params.companyFk) - .then(([[client]]) => { - if (!client) - throw new UserException('No client data found'); - - return client; - }); - }, - created() { - if (this.locale) - this.$i18n.locale = this.locale; - - const embeded = []; - this.files.map(file => { - embeded[file] = `file://${__dirname + file}`; - }); - this.embeded = embeded; - }, - data() { - return { - files: ['/assets/images/signature.png'] - }; - }, - methods: { - fetchClient(clientFk, companyFk) { - return database.pool.query( - `SELECT - c.id clientId, - u.lang locale, - m.code mandateCode, - c.socialName AS clientName, - c.street AS clientStreet, - c.postcode AS clientPostCode, - c.city AS clientCity, - c.fi, - p.name AS clientProvince, - ct.country AS clientCountry, - s.name AS supplierName, - s.street AS supplierStreet, - sc.country AS supplierCountry, - s.postCode AS supplierPostCode, - s.city AS supplierCity, - sp.name AS supplierProvince - FROM client c - JOIN account.user u ON u.id = c.id - JOIN country ct ON ct.id = c.countryFk - LEFT JOIN mandate m ON m.clientFk = c.id - AND m.companyFk = ? AND m.finished IS NULL - LEFT JOIN supplier s ON s.id = m.companyFk - LEFT JOIN country sc ON sc.id = s.countryFk - LEFT JOIN province sp ON sp.id = s.provinceFk - LEFT JOIN province p ON p.id = c.provinceFk - WHERE (m.companyFk = ? OR m.companyFk IS NULL) AND c.id = ? - ORDER BY m.created DESC LIMIT 1`, [companyFk, companyFk, clientFk]); - }, - dated: () => { - return strftime('%d-%m-%Y', new Date()); - }, - toISOString: date => { - return strftime('%d-%m-%Y', date); - }, - }, - components: { - 'report-header': require('../report-header'), - 'report-footer': require('../report-footer'), - }, -}; diff --git a/print/templates/reports/rpt-lcr/locale.js b/print/templates/reports/rpt-lcr/locale.js deleted file mode 100644 index 740b6d8f2..000000000 --- a/print/templates/reports/rpt-lcr/locale.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - messages: { - es: { - title: 'Autorisation pour débit', - Creditor: 'Tireur', - Deptor: 'Tiré', - Bank: 'Banque', - description: `Nous, soussignés, autorisons que tout effet émis par le tireur , susmentionné, et tiré sur notre Société, - soit automatiquement débité dans notre compte selon les suivants détails de domiciliation:`, - authorization: `Cette autorisation maintient sa validité jusqu'à à la réception de - nouvelles instructions.`, - supplier: { - name: 'Nom', - street: 'Adresse' - }, - bank: { - name: 'Nom', - street: 'Adresse', - account: 'RIB', - bankCode: 'Code banque', - agencyCode: 'Code agence', - accountNumber: 'Numero de compte', - ribKey: 'Clé RIB' - }, - client: { - name: 'Nom', - street: 'Adresse', - fi: 'Siren', - sign: 'Signature autorisée du tiré', - signDate: 'Lieu et date', - toCompleteByClient: 'À remplir par le débiteur', - }, - order: 'Ord. domiciliación {0}', - }, - }, -}; diff --git a/print/templates/reports/sepa-core/locale/es.yml b/print/templates/reports/sepa-core/locale/es.yml index d8955bc1f..66fe889b9 100644 --- a/print/templates/reports/sepa-core/locale/es.yml +++ b/print/templates/reports/sepa-core/locale/es.yml @@ -29,11 +29,13 @@ client: swift: Swift BIC accountNumber: Número de cuenta - IBAN accountHolder: "(Titular/es de la cuenta de cargo)" - accountNumberFormat: En España el IBAN consta de 24 posiciones comenzando siempre - por ES + accountNumberFormat: En {0} el IBAN consta de {1} posiciones comenzando siempre por {2} paymentType: Tipo de pago recurrent: Recurrente unique: Único signLocation: Fecha - Localidad sign: Firma del deudor y sello order: Ord. domiciliación {0} +Francia: Francia +España: España +Portugal: Portugal \ No newline at end of file diff --git a/print/templates/reports/sepa-core/locale/fr.yml b/print/templates/reports/sepa-core/locale/fr.yml index d8955bc1f..ec6fd11ab 100644 --- a/print/templates/reports/sepa-core/locale/fr.yml +++ b/print/templates/reports/sepa-core/locale/fr.yml @@ -1,39 +1,38 @@ -title: Orden de domiciliación de adeudo SEPA CORE -description: Mediante la firma de esta orden de domiciliación, el deudor autoriza - (A) al acreedor a enviar instrucciones a la entidad del deudor para adeudar su cuenta - y (B) a la entidad para efectuar los adeudos en su cuenta siguiendo las instrucciones - del acreedor.Como parte de sus derechos, el deudor está legitimado al reembolso - por su entidad en los términos y condiciones del contrato suscrito con la misma. - La solicitud de reembolso deberá efectuarse dentro de las ocho semanas que adeudo - en cuenta. Puede obtener información adicional sobre sus derechos en su entidad - financiera. -documentCopy: Debe llevar a su Entidad Bancaria una copia del documento firmado para - que lo registre y evitar la devolución. -mandatoryFields: TODOS LOS CAMPOS HAN DE SER CUMPLIMENTADOS OBLIGATORIAMENTE. -sendOrder: UNA VEZ FIRMADA ESTA ORDEN DE DOMICILIACIÓN DEBE SER ENVIADA AL ACREEDOR - PARA SU CUSTODIA Y ES RECOMENDABLE FACILITAR UNA COPIA A SU ENTIDAD BANCARIA. +title: Mandat de domiciliation Européene LCR +description: En signant ce formulaire de mandat, vous autorisez VERDNATURA LEVANTE SL + à envoyer des instructions à votre banque pour débiter votre compte, et (B) votre banque + à débiter votre compte conformément aux instructions de VERDNATURA LEVANTE SL. + Vous bénéficiez d’un droit au remboursement par votre banque selon les conditions décrites + dans la convention que vous avez passée avec elle. Toute demande de remboursement doit être + présentée dans les 8 semaines suivant la date de débit de votre compte. + Votre banque peut vous renseigner au sujet de vos droits relatifs à ce mandat. +documentCopy: Veuillez dater, signer et retourner ce document à votre banque. +mandatoryFields: TOUS LES CHAMPS DOIVENT ÊTRE REINSEGNÉS IMPÉRATIVEMENT. +sendOrder: APRÈS SIGNATURA, RENVOYER AU CRÉANCIER ET AU VOTRE ÉTABLISSEMENT FINANCIER. supplier: - toCompleteBySupplier: A cumplimentar por el acreedor - orderReference: Referencia de la orden de domiciliación - identifier: Identificador del acreedor - name: Nombre del acreedor - street: Dirección - location: CP - Población - Provincia - country: País + toCompleteBySupplier: Á compléter pour le créancier + orderReference: Numéro de référence du mandat + identifier: Identifiant créancier + name: Nom du céancier + street: Adresse + location: CP - Commune - Départament + country: Pays client: - toCompleteByClient: A cumplimentar por el deudor - name: Nombre del deudor/es - street: Dirección del deudor - location: CP - Población - Provincia - country: País del deudor + toCompleteByClient: Á compléter pour le débiteur + name: Nom du débiteur(s) + street: Adresse du(des) débiteur(s) + location: CP - Commune - Départament + country: País du(des) débiteur(s) swift: Swift BIC - accountNumber: Número de cuenta - IBAN - accountHolder: "(Titular/es de la cuenta de cargo)" - accountNumberFormat: En España el IBAN consta de 24 posiciones comenzando siempre - por ES - paymentType: Tipo de pago - recurrent: Recurrente - unique: Único - signLocation: Fecha - Localidad - sign: Firma del deudor y sello -order: Ord. domiciliación {0} + accountNumber: Numéro de compte - IBAN + accountHolder: (Débiteur(s) de compte) + accountNumberFormat: En {0} l'IBAN compte {1} postes commençant toujours par {2} + paymentType: Type de paiemen + recurrent: Versement périodique + unique: Paiement unique + signLocation: Date - Commune + sign: Signature du débiteur et tampon +order: Réf. mandat {0} +Francia: France +España: Espagne +Portugal: Portugal \ No newline at end of file diff --git a/print/templates/reports/sepa-core/locale/pt.yml b/print/templates/reports/sepa-core/locale/pt.yml index 2e2d08937..5b71fbdf5 100644 --- a/print/templates/reports/sepa-core/locale/pt.yml +++ b/print/templates/reports/sepa-core/locale/pt.yml @@ -22,18 +22,20 @@ supplier: country: País client: toCompleteByClient: A preencher pelo devedor - name: Nome do devedor (Titular da conta) + name: Nome do devedor street: Dirección del deudor location: Cod. Postal - Município - Distrito country: País do devedor swift: Swift BIC accountNumber: Número de Conta IBAN - accountHolder: "(Titular/es de la cuenta de cargo)" - accountNumberFormat: Em Portugal o IBAN é composto por 25 dígitos e começa sempre - por PT + accountHolder: "(Titular(es) da conta)" + accountNumberFormat: Em {0} o IBAN é composto pelo {1} dígitos e começa sempre pelo {2} paymentType: Tipos de pagamento Pagamento recurrent: Recorrente unique: Pagamento pontual signLocation: Data - Localidade sign: Assinatura e carimbo do devedor order: Referência da ordem {0} +Francia: França +España: Espanha +Portugal: Portugal \ No newline at end of file diff --git a/print/templates/reports/sepa-core/sepa-core.html b/print/templates/reports/sepa-core/sepa-core.html index 16fdc163c..69bf2651b 100644 --- a/print/templates/reports/sepa-core/sepa-core.html +++ b/print/templates/reports/sepa-core/sepa-core.html @@ -32,7 +32,10 @@ {{$t('supplier.identifier')}} - ES89000B97367486 + +
        ES89000B97367486
        +
        B97367486-000
        + {{$t('supplier.name')}} @@ -110,14 +113,19 @@
        {{client.countryCode.substr(0, 1)}} {{client.countryCode.substr(1, 1)}} - +
        - {{$t('client.accountNumberFormat')}} + {{$t('client.accountNumberFormat', [ + $t(`${client.country}`), + client.ibanLength, + client.countryCode + ])}} +
        @@ -145,7 +153,7 @@ {{$t('client.signLocation')}} - {{dated}}, {{supplier.province}} + {{dated}}, {{client.province}} {{$t('client.sign')}}