diff --git a/db/changes/10330-jun2021/01-ticket_componentMakeUpdate.sql b/db/changes/10330-jun2021/01-ticket_componentMakeUpdate.sql
new file mode 100644
index 000000000..ac3e85f08
--- /dev/null
+++ b/db/changes/10330-jun2021/01-ticket_componentMakeUpdate.sql
@@ -0,0 +1,103 @@
+DROP PROCEDURE IF EXISTS `vn`.`ticket_componentMakeUpdate`;
+
+DELIMITER $$
+$$
+
+CREATE
+ DEFINER = root@`%` PROCEDURE `vn`.`ticket_componentMakeUpdate`(IN vTicketFk INT, IN vClientFk INT,
+ IN vNickname VARCHAR(50), IN vAgencyModeFk INT,
+ IN vAddressFk INT, IN vZoneFk INT, IN vWarehouseFk TINYINT,
+ IN vCompanyFk SMALLINT, IN vShipped DATETIME,
+ IN vLanded DATE, IN vIsDeleted TINYINT(1),
+ IN vHasToBeUnrouted TINYINT(1), IN vOption INT)
+BEGIN
+/**
+ * Modifica en el ticket los campos que se le pasan por parámetro
+ * y cambia sus componentes
+ *
+ * @param vTicketFk Id del ticket a modificar
+ * @param vClientFk nuevo cliente
+ * @param vNickname nuevo alias
+ * @param vAgencyModeFk nueva agencia
+ * @param vAddressFk nuevo consignatario
+ * @param vZoneFk nueva zona
+ * @param vWarehouseFk nuevo almacen
+ * @param vCompanyFk nueva empresa
+ * @param vShipped nueva fecha del envio de mercancia
+ * @param vLanded nueva fecha de recepcion de mercancia
+ * @param vIsDeleted si se borra el ticket
+ * @param vHasToBeUnrouted si se le elimina la ruta al ticket
+ * @param vOption opcion para el case del proc ticketComponentUpdateSale
+ */
+ DECLARE vPrice DECIMAL(10,2);
+ DECLARE vBonus DECIMAL(10,2);
+ DECLARE EXIT HANDLER FOR SQLEXCEPTION
+ BEGIN
+ ROLLBACK;
+ RESIGNAL;
+ END;
+
+ CALL ticket_componentPreview (vTicketFk, vLanded, vAddressFk, vZoneFk, vWarehouseFk);
+
+ START TRANSACTION;
+
+ IF (SELECT addressFk FROM ticket WHERE id = vTicketFk) <> vAddressFk THEN
+
+ UPDATE ticket t
+ JOIN address a ON a.id = vAddressFk
+ SET t.nickname = a.nickname
+ WHERE t.id = vTicketFk;
+
+ END IF;
+
+ CALL zone_getShippedWarehouse(vlanded, vAddressFk, vAgencyModeFk);
+
+ SELECT zoneFk, price, bonus INTO vZoneFk, vPrice, vBonus
+ FROM tmp.zoneGetShipped
+ WHERE shipped BETWEEN DATE(vShipped) AND util.dayEnd(vShipped) AND warehouseFk = vWarehouseFk LIMIT 1;
+
+ UPDATE ticket t
+ SET
+ t.clientFk = vClientFk,
+ t.nickname = vNickname,
+ t.agencyModeFk = vAgencyModeFk,
+ t.addressFk = vAddressFk,
+ t.zoneFk = vZoneFk,
+ t.zonePrice = vPrice,
+ t.zoneBonus = vBonus,
+ t.warehouseFk = vWarehouseFk,
+ t.companyFk = vCompanyFk,
+ t.landed = vLanded,
+ t.shipped = vShipped,
+ t.isDeleted = vIsDeleted
+ WHERE
+ t.id = vTicketFk;
+
+ IF vHasToBeUnrouted THEN
+ UPDATE ticket t SET t.routeFk = NULL
+ WHERE t.id = vTicketFk;
+ END IF;
+
+ IF vOption <> 8 THEN
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale;
+ CREATE TEMPORARY TABLE tmp.sale
+ (PRIMARY KEY (saleFk))
+ ENGINE = MEMORY
+ SELECT id AS saleFk, vWarehouseFk warehouseFk
+ FROM sale s WHERE s.ticketFk = vTicketFk;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
+ CREATE TEMPORARY TABLE tmp.ticketComponent
+ SELECT * FROM tmp.ticketComponentPreview;
+
+ CALL ticketComponentUpdateSale (vOption);
+
+ DROP TEMPORARY TABLE tmp.sale;
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
+ END IF;
+ COMMIT;
+
+ DROP TEMPORARY TABLE tmp.zoneGetShipped, tmp.ticketComponentPreview;
+END$$
+DELIMITER ;
+
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index a31f11b56..f65ebc95d 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -837,7 +837,8 @@ export default {
saveButton: 'vn-worker-pbx button[type=submit]'
},
workerTimeControl: {
- timeDialog: '.vn-dialog.shown vn-input-time[ng-model="$ctrl.newTime"]',
+ dialogTimeInput: '.vn-dialog.shown vn-input-time[ng-model="$ctrl.newTimeEntry.timed"]',
+ dialogTimeDirection: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.newTimeEntry.direction"]',
mondayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(1) > vn-icon-button',
tuesdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(2) > vn-icon-button',
wednesdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(3) > vn-icon-button',
@@ -845,35 +846,35 @@ export default {
fridayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(5) > vn-icon-button',
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',
- 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',
+ firstEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ firstEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ firstEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ firstEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ firstEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ firstEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ firstEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > vn-chip > div:nth-child(2)',
+ secondEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ secondEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ secondEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ secondEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ secondEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ secondEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ secondEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > vn-chip > div:nth-child(2)',
+ thirdEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > vn-chip > div:nth-child(2)',
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',
+ thirdEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > vn-chip > div:nth-child(2)',
+ thirdEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > vn-chip > div:nth-child(2)',
+ thirdEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > vn-chip > div:nth-child(2)',
+ thirdEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > vn-chip > div:nth-child(2)',
+ thirdEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > vn-chip > div:nth-child(2)',
+ thirdEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > vn-chip > div:nth-child(2)',
+ fourthEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > vn-chip > div:nth-child(2)',
+ fourthEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > vn-chip > div:nth-child(2)',
+ fourthEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > vn-chip > div:nth-child(2)',
+ fourthEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > vn-chip > div:nth-child(2)',
+ fourthEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > vn-chip > div:nth-child(2)',
+ fourthEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > vn-chip > div:nth-child(2)',
+ fourthEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > vn-chip > div:nth-child(2)',
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)',
diff --git a/e2e/paths/03-worker/04_time_control.spec.js b/e2e/paths/03-worker/04_time_control.spec.js
index dbcebff08..3f5653aee 100644
--- a/e2e/paths/03-worker/04_time_control.spec.js
+++ b/e2e/paths/03-worker/04_time_control.spec.js
@@ -22,7 +22,8 @@ describe('Worker time control path', () => {
const scanTime = '07:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText');
@@ -33,7 +34,8 @@ describe('Worker time control path', () => {
const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfMonday, 'innerText');
@@ -44,7 +46,8 @@ describe('Worker time control path', () => {
const scanTime = '18:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText');
@@ -66,7 +69,8 @@ describe('Worker time control path', () => {
const scanTime = '14:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText');
@@ -77,7 +81,8 @@ describe('Worker time control path', () => {
const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfMonday, 'innerText');
@@ -103,7 +108,8 @@ describe('Worker time control path', () => {
const scanTime = '08:00';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfTuesday, 'innerText');
@@ -114,7 +120,8 @@ describe('Worker time control path', () => {
const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfTuesday, 'innerText');
@@ -125,7 +132,8 @@ describe('Worker time control path', () => {
const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfTuesday, 'innerText');
@@ -136,7 +144,8 @@ describe('Worker time control path', () => {
const scanTime = '16:00';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfTuesday, 'innerText');
@@ -153,7 +162,8 @@ describe('Worker time control path', () => {
const scanTime = '09:00';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfWednesday, 'innerText');
@@ -164,7 +174,8 @@ describe('Worker time control path', () => {
const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfWednesday, 'innerText');
@@ -175,7 +186,8 @@ describe('Worker time control path', () => {
const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfWednesday, 'innerText');
@@ -186,7 +198,8 @@ describe('Worker time control path', () => {
const scanTime = '17:00';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfWednesday, 'innerText');
@@ -203,7 +216,8 @@ describe('Worker time control path', () => {
const scanTime = '09:59';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfThursday, 'innerText');
@@ -213,7 +227,8 @@ describe('Worker time control path', () => {
it(`should joyfully scan out Hank Pym for break`, async() => {
const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfThursday, 'innerText');
@@ -223,7 +238,8 @@ describe('Worker time control path', () => {
it(`should joyfully scan in Hank Pym from the break`, async() => {
const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfThursday, 'innerText');
@@ -233,7 +249,8 @@ describe('Worker time control path', () => {
it(`should joyfully scan out Hank Pym for the day`, async() => {
const scanTime = '17:59';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfThursday, 'innerText');
@@ -249,7 +266,8 @@ describe('Worker time control path', () => {
it('should smilingly scan in Hank Pym', async() => {
const scanTime = '07:30';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfFriday, 'innerText');
@@ -259,7 +277,8 @@ describe('Worker time control path', () => {
it(`should smilingly scan out Hank Pym for break`, async() => {
const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfFriday, 'innerText');
@@ -269,7 +288,8 @@ describe('Worker time control path', () => {
it(`should smilingly scan in Hank Pym from the break`, async() => {
const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'intermediate');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfFriday, 'innerText');
@@ -279,7 +299,8 @@ describe('Worker time control path', () => {
it(`should smilingly scan out Hank Pym for the day`, async() => {
const scanTime = '15:30';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfFriday, 'innerText');
@@ -310,8 +331,10 @@ describe('Worker time control path', () => {
it('should lovingly scan in Hank Pym', async() => {
const scanTime = '06:00';
+ await page.waitForTimeout(1000); // without this timeout the dialog doesn't pop up
await page.waitToClick(selectors.workerTimeControl.saturdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfSaturday, 'innerText');
@@ -321,7 +344,8 @@ describe('Worker time control path', () => {
it(`should lovingly scan out Hank Pym for the day with no break to leave a bit early`, async() => {
const scanTime = '13:40';
await page.waitToClick(selectors.workerTimeControl.saturdayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfSaturday, 'innerText');
@@ -337,7 +361,8 @@ describe('Worker time control path', () => {
it('should gladly scan in Hank Pym', async() => {
const scanTime = '05:00';
await page.waitToClick(selectors.workerTimeControl.sundayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfSunday, 'innerText');
@@ -347,7 +372,8 @@ describe('Worker time control path', () => {
it(`should gladly scan out Hank Pym for the day with no break to leave a bit early`, async() => {
const scanTime = '12:40';
await page.waitToClick(selectors.workerTimeControl.sundayAddTimeButton);
- await page.pickTime(selectors.workerTimeControl.timeDialog, scanTime);
+ await page.pickTime(selectors.workerTimeControl.dialogTimeInput, scanTime);
+ await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfSunday, 'innerText');
diff --git a/e2e/paths/14-account/01_create_and_basic_data.spec.js b/e2e/paths/14-account/01_create_and_basic_data.spec.js
index 15c9118d1..5a07119e7 100644
--- a/e2e/paths/14-account/01_create_and_basic_data.spec.js
+++ b/e2e/paths/14-account/01_create_and_basic_data.spec.js
@@ -65,6 +65,7 @@ describe('Account create and basic data path', () => {
describe('Descriptor option', () => {
describe('Edit role', () => {
it('should edit the role using the descriptor menu', async() => {
+ await page.waitForTimeout(1000); // sometimes descriptor fails to load it's functionalities without this timeout
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.changeRole);
await page.autocompleteSearch(selectors.accountDescriptor.newRole, 'adminBoss');
diff --git a/front/core/components/chip/index.html b/front/core/components/chip/index.html
index 38a56923d..7cd56031c 100644
--- a/front/core/components/chip/index.html
+++ b/front/core/components/chip/index.html
@@ -1,6 +1,12 @@
+
+
diff --git a/front/core/components/chip/index.js b/front/core/components/chip/index.js
index 6cffecc2b..ff0f19ba2 100644
--- a/front/core/components/chip/index.js
+++ b/front/core/components/chip/index.js
@@ -3,16 +3,19 @@ import Component from '../../lib/component';
import './style.scss';
export default class Chip extends Component {
- onRemove() {
- if (!this.disabled) this.emit('remove');
+ onRemove($event) {
+ if (!this.disabled) this.emit('remove', {$event});
}
}
Chip.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnChip', {
template: require('./index.html'),
+ transclude: {
+ prepend: '?prepend',
+ append: '?append'
+ },
controller: Chip,
- transclude: true,
bindings: {
disabled: '',
removable: ''
diff --git a/front/core/components/chip/index.spec.js b/front/core/components/chip/index.spec.js
index fc93527d6..36b0b4352 100644
--- a/front/core/components/chip/index.spec.js
+++ b/front/core/components/chip/index.spec.js
@@ -18,9 +18,14 @@ describe('Component vnChip', () => {
it(`should emit remove event`, () => {
controller.emit = () => {};
jest.spyOn(controller, 'emit');
- controller.onRemove();
- expect(controller.emit).toHaveBeenCalledWith('remove');
+ const $event = new Event('click');
+ const target = document.createElement('div');
+ target.dispatchEvent($event);
+
+ controller.onRemove($event);
+
+ expect(controller.emit).toHaveBeenCalledWith('remove', {$event});
});
});
});
diff --git a/front/core/components/chip/style.scss b/front/core/components/chip/style.scss
index f6e4a388f..d1632890c 100644
--- a/front/core/components/chip/style.scss
+++ b/front/core/components/chip/style.scss
@@ -1,4 +1,5 @@
@import "variables";
+@import "effects";
vn-chip {
border-radius: 16px;
@@ -24,25 +25,47 @@ vn-chip {
&.transparent {
background-color: transparent;
}
- &.colored {
+ &.colored,
+ &.colored.clickable:hover,
+ &.colored.clickable:focus {
background-color: $color-main;
color: $color-font-bg;
}
- &.notice {
- background-color: $color-notice-medium
+
+ &.notice,
+ &.notice.clickable:hover,
+ &.notice.clickable:focus {
+ background-color: $color-notice-medium;
}
- &.success {
+ &.success,
+ &.success.clickable:hover,
+ &.success.clickable:focus {
background-color: $color-success-medium;
}
- &.warning {
+ &.warning,
+ &.warning.clickable:hover,
+ &.warning.clickable:focus {
background-color: $color-main-medium;
}
- &.alert {
+ &.alert,
+ &.alert.clickable:hover,
+ &.alert.clickable:focus {
background-color: $color-alert-medium;
}
- &.message {
+ &.message,
+ &.message.clickable:hover,
+ &.message.clickable:focus {
color: $color-font-dark;
- background-color: $color-bg-dark
+ background-color: $color-bg-dark;
+ }
+ &.clickable {
+ @extend %clickable;
+ opacity: 0.8;
+
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
}
& > div {
@@ -75,6 +98,20 @@ vn-chip {
opacity: 1;
}
}
+
+ & > .prepend {
+ padding: 0 5px;
+ padding-right: 0;
+
+ &:empty {display:none;}
+ }
+ & > .append {
+ padding: 0 5px;
+ padding-left: 0;
+
+ &:empty {display:none;}
+ }
+
}
vn-avatar {
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index 6bdaddd9b..9bef13527 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -97,5 +97,6 @@
"Role name must be written in camelCase": "Role name must be written in camelCase",
"Client assignment has changed": "I did change the salesperson ~*\"<{{previousWorkerName}}>\"*~ by *\"<{{currentWorkerName}}>\"* from the client [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "None",
- "error densidad = 0": "error densidad = 0"
+ "error densidad = 0": "error densidad = 0",
+ "nickname": "nickname"
}
\ No newline at end of file
diff --git a/modules/client/front/summary/index.html b/modules/client/front/summary/index.html
index 956f9d198..a93aec4ea 100644
--- a/modules/client/front/summary/index.html
+++ b/modules/client/front/summary/index.html
@@ -1,11 +1,12 @@
+ order="shipped DESC">
@@ -292,9 +293,9 @@
Id
Client
- Salesperson
+ Packages
Date
- State
+ State
Total
@@ -312,13 +313,8 @@
{{::ticket.nickname}}
-
-
- {{::ticket.userName | dashIfEmpty}}
-
+
+ {{::ticket.packages}}
@@ -336,7 +332,7 @@
- {{::ticket.state}}
+ {{::ticket.ticketState.state.name}}
diff --git a/modules/client/front/summary/index.js b/modules/client/front/summary/index.js
index 40c000118..113fd2ab2 100644
--- a/modules/client/front/summary/index.js
+++ b/modules/client/front/summary/index.js
@@ -3,6 +3,21 @@ import Summary from 'salix/components/summary';
import './style.scss';
class Controller extends Summary {
+ constructor($element, $) {
+ super($element, $);
+
+ this.ticketFilter = {
+ include: {
+ relation: 'ticketState',
+ scope: {
+ fields: ['stateFk', 'code', 'alertLevel'],
+ include: {
+ relation: 'state'
+ }
+ }
+ }
+ };
+ }
$onChanges() {
if (!this.client)
return;
@@ -18,6 +33,7 @@ class Controller extends Summary {
}
});
}
+
get isEmployee() {
return this.aclService.hasAny(['employee']);
}
@@ -41,13 +57,15 @@ class Controller extends Summary {
}
stateColor(ticket) {
- if (ticket.alertLevelCode === 'OK')
+ const ticketState = ticket.ticketState;
+
+ if (ticketState.code === 'OK')
return 'success';
- else if (ticket.alertLevelCode === 'FREE')
+ else if (ticketState.code === 'FREE')
return 'notice';
- else if (ticket.alertLevel === 1)
+ else if (ticketState.alertLevel === 1)
return 'warning';
- else if (ticket.alertLevel === 0)
+ else if (ticketState.alertLevel === 0)
return 'alert';
}
diff --git a/modules/client/front/summary/index.spec.js b/modules/client/front/summary/index.spec.js
index 08f3f8554..397bb4240 100644
--- a/modules/client/front/summary/index.spec.js
+++ b/modules/client/front/summary/index.spec.js
@@ -76,25 +76,25 @@ describe('Client', () => {
describe('stateColor()', () => {
it('should return "success" when the alertLevelCode property is "OK"', () => {
- const result = controller.stateColor({alertLevelCode: 'OK'});
+ const result = controller.stateColor({ticketState: {code: 'OK'}});
expect(result).toEqual('success');
});
it('should return "notice" when the alertLevelCode property is "FREE"', () => {
- const result = controller.stateColor({alertLevelCode: 'FREE'});
+ const result = controller.stateColor({ticketState: {code: 'FREE'}});
expect(result).toEqual('notice');
});
it('should return "warning" when the alertLevel property is "1', () => {
- const result = controller.stateColor({alertLevel: 1});
+ const result = controller.stateColor({ticketState: {code: 'PACKING', alertLevel: 1}});
expect(result).toEqual('warning');
});
it('should return "alert" when the alertLevel property is "0"', () => {
- const result = controller.stateColor({alertLevel: 0});
+ const result = controller.stateColor({ticketState: {code: 'FIXING', alertLevel: 0}});
expect(result).toEqual('alert');
});
diff --git a/modules/invoiceIn/back/methods/invoice-in/filter.js b/modules/invoiceIn/back/methods/invoice-in/filter.js
index b480df64a..3b9330eb9 100644
--- a/modules/invoiceIn/back/methods/invoice-in/filter.js
+++ b/modules/invoiceIn/back/methods/invoice-in/filter.js
@@ -109,8 +109,9 @@ module.exports = Self => {
return {'ii.created': {gte: value}};
case 'to':
return {'ii.created': {lte: value}};
- case 'account':
case 'fi':
+ return {'s.nif': value};
+ case 'account':
return {[`s.${param}`]: value};
case 'supplierRef':
case 'supplierFk':
diff --git a/modules/invoiceIn/front/index/index.html b/modules/invoiceIn/front/index/index.html
index 3d9b787d9..62074be33 100644
--- a/modules/invoiceIn/front/index/index.html
+++ b/modules/invoiceIn/front/index/index.html
@@ -54,19 +54,22 @@
-
-
+ -->
+
+
diff --git a/modules/invoiceIn/front/search-panel/index.html b/modules/invoiceIn/front/search-panel/index.html
index 90bb73725..d26bc063e 100644
--- a/modules/invoiceIn/front/search-panel/index.html
+++ b/modules/invoiceIn/front/search-panel/index.html
@@ -27,11 +27,12 @@
label="Account"
ng-model="filter.account">
-
-
+ ng-model="filter.amount"
+ step="0.01">
+
{{::invoiceOut.created | date:'dd/MM/yyyy' | dashIfEmpty}}
{{::invoiceOut.companyCode | dashIfEmpty}}
{{::invoiceOut.dued | date:'dd/MM/yyyy' | dashIfEmpty}}
-
+
-
+
{
description: 'The client id',
required: true
},
+ {
+ arg: 'nickname',
+ type: 'string',
+ description: 'The client nickname'
+ },
{
arg: 'agencyModeFk',
type: 'number',
@@ -145,10 +150,11 @@ module.exports = Self => {
// Force to unroute ticket
const hasToBeUnrouted = true;
- const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
+ const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
const res = await Self.rawSql(query, [
args.id,
args.clientFk,
+ args.nickname,
args.agencyModeFk,
args.addressFk,
args.zoneFk,
diff --git a/modules/ticket/front/basic-data/step-one/index.html b/modules/ticket/front/basic-data/step-one/index.html
index 98d92e7e2..24ec509a2 100644
--- a/modules/ticket/front/basic-data/step-one/index.html
+++ b/modules/ticket/front/basic-data/step-one/index.html
@@ -20,6 +20,16 @@
initial-data="$ctrl.clientId"
order="id">
+
+
+
+
-
-
+ ng-model="$ctrl.ticket.nickname">
+
{
+ this.ticket.nickname = res.data.nickname;
+ });
+ }
+
async onStepChange() {
if (this.isFormInvalid()) {
return this.vnApp.showError(
diff --git a/modules/ticket/front/basic-data/step-two/index.js b/modules/ticket/front/basic-data/step-two/index.js
index c578106ed..ebb93bb3d 100644
--- a/modules/ticket/front/basic-data/step-two/index.js
+++ b/modules/ticket/front/basic-data/step-two/index.js
@@ -71,6 +71,7 @@ class Controller extends Component {
let query = `tickets/${this.ticket.id}/componentUpdate`;
let params = {
clientFk: this.ticket.clientFk,
+ nickname: this.ticket.nickname,
agencyModeFk: this.ticket.agencyModeFk,
addressFk: this.ticket.addressFk,
zoneFk: this.ticket.zoneFk,
diff --git a/modules/worker/back/methods/worker-time-control/addTimeEntry.js b/modules/worker/back/methods/worker-time-control/addTimeEntry.js
index 86030d713..2079a62a3 100644
--- a/modules/worker/back/methods/worker-time-control/addTimeEntry.js
+++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js
@@ -5,38 +5,54 @@ module.exports = Self => {
description: 'Adds a new hour registry',
accessType: 'WRITE',
accepts: [{
- arg: 'data',
- type: 'object',
- required: true,
- description: 'workerFk, timed',
- http: {source: 'body'}
+ arg: 'id',
+ type: 'number',
+ description: 'The worker id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'timed',
+ type: 'date',
+ required: true
+ },
+ {
+ arg: 'direction',
+ type: 'string',
+ required: true
}],
returns: [{
type: 'Object',
root: true
}],
http: {
- path: `/addTimeEntry`,
+ path: `/:id/addTimeEntry`,
verb: 'POST'
}
});
- 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}});
- const isSubordinate = await Worker.isSubordinate(ctx, data.workerFk);
- const isTeamBoss = await Self.app.models.Account.hasRole(myUserId, 'teamBoss');
+ Self.addTimeEntry = async(ctx, workerId, options) => {
+ const models = Self.app.models;
+ const args = ctx.args;
+ const currentUserId = ctx.req.accessToken.userId;
- if (isSubordinate === false || (isSubordinate && myWorker.id == data.workerFk && !isTeamBoss))
+ let myOptions = {};
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const isSubordinate = await models.Worker.isSubordinate(ctx, workerId, myOptions);
+ const isTeamBoss = await models.Account.hasRole(currentUserId, 'teamBoss', myOptions);
+ const isHimself = currentUserId == workerId;
+
+ if (isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss))
throw new UserError(`You don't have enough privileges`);
- const subordinate = await Worker.findById(data.workerFk);
- const timed = new Date(data.timed);
+ const timed = new Date(args.timed);
- let [result] = await Self.rawSql('SELECT vn.workerTimeControl_add(?, ?, ?, ?) AS id', [
- subordinate.userFk, null, timed, true]);
-
- return result;
+ return models.WorkerTimeControl.create({
+ userFk: workerId,
+ direction: args.direction,
+ timed: timed,
+ manual: true
+ }, myOptions);
};
};
diff --git a/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js b/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js
index 97637d197..23e4c5fff 100644
--- a/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js
+++ b/modules/worker/back/methods/worker-time-control/deleteTimeEntry.js
@@ -21,21 +21,24 @@ module.exports = Self => {
}
});
- Self.deleteTimeEntry = async(ctx, id) => {
+ Self.deleteTimeEntry = async(ctx, id, options) => {
const currentUserId = ctx.req.accessToken.userId;
- const workerModel = Self.app.models.Worker;
+ const models = Self.app.models;
- const targetTimeEntry = await Self.findById(id);
- const isSubordinate = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk);
- const isTeamBoss = await Self.app.models.Account.hasRole(currentUserId, 'teamBoss');
+ let myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const targetTimeEntry = await Self.findById(id, null, myOptions);
+ const isSubordinate = await models.Worker.isSubordinate(ctx, targetTimeEntry.userFk, myOptions);
+ const isTeamBoss = await models.Account.hasRole(currentUserId, 'teamBoss', myOptions);
const isHimself = currentUserId == targetTimeEntry.userFk;
- const notAllowed = isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss);
-
- if (notAllowed)
+ if (isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss))
throw new UserError(`You don't have enough privileges`);
return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [
- targetTimeEntry.userFk, targetTimeEntry.timed]);
+ targetTimeEntry.userFk, targetTimeEntry.timed], myOptions);
};
};
diff --git a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
index 0f055bdc5..a50182f1b 100644
--- a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
+++ b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
@@ -1,5 +1,6 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
+const models = app.models;
describe('workerTimeControl add/delete timeEntry()', () => {
const HHRRId = 37;
@@ -12,19 +13,6 @@ describe('workerTimeControl add/delete timeEntry()', () => {
};
let ctx = {req: activeCtx};
- let timeEntry;
- let createdTimeEntry;
-
- afterEach(async() => {
- if (createdTimeEntry) {
- try {
- await app.models.WorkerTimeControl.destroyById(createdTimeEntry.id);
- } catch (error) {
- console.error(error);
- }
- }
- });
-
beforeAll(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
@@ -33,14 +21,13 @@ describe('workerTimeControl add/delete timeEntry()', () => {
it('should fail to add a time entry if the target user is not a subordinate', async() => {
activeCtx.accessToken.userId = employeeId;
+ const workerId = 2;
+
let error;
- let data = {
- workerFk: 2,
- timed: new Date()
- };
try {
- await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
+ ctx.args = {timed: new Date(), direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId);
} catch (e) {
error = e;
}
@@ -52,14 +39,12 @@ describe('workerTimeControl add/delete timeEntry()', () => {
it('should fail to add if the current and the target user are the same and is not team boss', async() => {
activeCtx.accessToken.userId = employeeId;
+ const workerId = employeeId;
let error;
- let data = {
- workerFk: 1,
- timed: new Date()
- };
try {
- await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
+ ctx.args = {timed: new Date(), direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId);
} catch (e) {
error = e;
}
@@ -71,41 +56,49 @@ describe('workerTimeControl add/delete timeEntry()', () => {
it('should add if the current user is team boss and the target user is a himself', async() => {
activeCtx.accessToken.userId = teamBossId;
- let todayAtSix = new Date();
- todayAtSix.setHours(18, 30, 0, 0);
+ const workerId = teamBossId;
- let data = {
- workerFk: teamBossId,
- timed: todayAtSix
- };
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
- timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
+ const todayAtSix = new Date();
+ todayAtSix.setHours(18, 30, 0, 0);
- createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
+ ctx.args = {timed: todayAtSix, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- expect(createdTimeEntry).toBeDefined();
+ expect(createdTimeEntry.id).toBeDefined();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
});
it('should try but fail to delete his own time entry', async() => {
activeCtx.accessToken.userId = salesBossId;
+ const workerId = salesBossId;
+
let error;
- let todayAtSeven = new Date();
- todayAtSeven.setHours(19, 30, 0, 0);
-
- let data = {
- workerFk: salesPersonId,
- timed: todayAtSeven
- };
-
- timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
-
- createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
-
+ const tx = await models.WorkerTimeControl.beginTransaction({});
try {
+ const options = {transaction: tx};
+
+ const todayAtSeven = new Date();
+ todayAtSeven.setHours(19, 30, 0, 0);
+
+ ctx.args = {timed: todayAtSeven, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
activeCtx.accessToken.userId = salesPersonId;
- await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id);
+ await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
+
+ await tx.rollback();
} catch (e) {
error = e;
+ await tx.rollback();
}
expect(error).toBeDefined();
@@ -115,49 +108,84 @@ describe('workerTimeControl add/delete timeEntry()', () => {
it('should delete the created time entry for the team boss as himself', async() => {
activeCtx.accessToken.userId = teamBossId;
+ const workerId = teamBossId;
- let todayAtFive = new Date();
- todayAtFive.setHours(17, 30, 0, 0);
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
- let data = {
- workerFk: teamBossId,
- timed: todayAtFive
- };
+ const todayAtFive = new Date();
+ todayAtFive.setHours(17, 30, 0, 0);
- timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
+ ctx.args = {timed: todayAtFive, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
+ expect(createdTimeEntry.id).toBeDefined();
- expect(createdTimeEntry).toBeDefined();
+ await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
- await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id);
+ const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
- createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
-
- expect(createdTimeEntry).toBeNull();
+ expect(deletedTimeEntry).toBeNull();
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
});
it('should delete the created time entry for the team boss as HHRR', async() => {
activeCtx.accessToken.userId = HHRRId;
+ const workerId = teamBossId;
- let todayAtFive = new Date();
- todayAtFive.setHours(17, 30, 0, 0);
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
- let data = {
- workerFk: teamBossId,
- timed: todayAtFive
- };
+ const todayAtFive = new Date();
+ todayAtFive.setHours(17, 30, 0, 0);
- timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
+ ctx.args = {timed: todayAtFive, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
+ expect(createdTimeEntry.id).toBeDefined();
- expect(createdTimeEntry).toBeDefined();
+ await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
- await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id);
+ const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
- createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
+ expect(deletedTimeEntry).toBeNull();
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
- expect(createdTimeEntry).toBeNull();
+ it('should edit the created time entry for the team boss as HHRR', async() => {
+ activeCtx.accessToken.userId = HHRRId;
+ const workerId = teamBossId;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
+
+ const todayAtFive = new Date();
+ todayAtFive.setHours(17, 30, 0, 0);
+
+ ctx.args = {timed: todayAtFive, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ expect(createdTimeEntry.id).toBeDefined();
+
+ ctx.args = {direction: 'out'};
+ const updatedTimeEntry = await models.WorkerTimeControl.updateTimeEntry(ctx, createdTimeEntry.id, options);
+
+ expect(updatedTimeEntry.direction).toEqual('out');
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
});
});
diff --git a/modules/worker/back/methods/worker-time-control/updateTimeEntry.js b/modules/worker/back/methods/worker-time-control/updateTimeEntry.js
new file mode 100644
index 000000000..abeda7f8e
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/updateTimeEntry.js
@@ -0,0 +1,53 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('updateTimeEntry', {
+ description: 'Updates a 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'}
+ },
+ {
+ arg: 'direction',
+ type: 'string',
+ required: true
+ }],
+ returns: {
+ type: 'boolean',
+ root: true
+ },
+ http: {
+ path: `/:id/updateTimeEntry`,
+ verb: 'POST'
+ }
+ });
+
+ Self.updateTimeEntry = async(ctx, id, options) => {
+ const currentUserId = ctx.req.accessToken.userId;
+ const models = Self.app.models;
+ const args = ctx.args;
+
+ let myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const targetTimeEntry = await Self.findById(id, null, myOptions);
+ const isSubordinate = await models.Worker.isSubordinate(ctx, targetTimeEntry.userFk, myOptions);
+ const isTeamBoss = await models.Account.hasRole(currentUserId, 'teamBoss', myOptions);
+ const isHimself = currentUserId == targetTimeEntry.userFk;
+
+ const notAllowed = isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss);
+
+ if (notAllowed)
+ throw new UserError(`You don't have enough privileges`);
+
+ return targetTimeEntry.updateAttributes({
+ direction: args.direction
+ }, myOptions);
+ };
+};
diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js
index f0191c36f..45f4e2194 100644
--- a/modules/worker/back/models/worker-time-control.js
+++ b/modules/worker/back/models/worker-time-control.js
@@ -4,6 +4,7 @@ module.exports = Self => {
require('../methods/worker-time-control/filter')(Self);
require('../methods/worker-time-control/addTimeEntry')(Self);
require('../methods/worker-time-control/deleteTimeEntry')(Self);
+ require('../methods/worker-time-control/updateTimeEntry')(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 5212222bd..ab07802ca 100644
--- a/modules/worker/back/models/worker-time-control.json
+++ b/modules/worker/back/models/worker-time-control.json
@@ -9,16 +9,16 @@
"properties": {
"id": {
"id": true,
- "type": "Number"
+ "type": "number"
},
"timed": {
- "type": "Date"
+ "type": "date"
},
"manual": {
- "type": "Boolean"
+ "type": "boolean"
},
"order": {
- "type": "Number"
+ "type": "number"
},
"direction": {
"type": "string"
diff --git a/modules/worker/front/calendar/index.html b/modules/worker/front/calendar/index.html
index f012e8e55..42d363952 100644
--- a/modules/worker/front/calendar/index.html
+++ b/modules/worker/front/calendar/index.html
@@ -23,7 +23,7 @@
-
{{'Contract' | translate}} ID: {{$ctrl.businessId}}
+
{{'Contract' | translate}} #{{$ctrl.businessId}}
{{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed}}
{{'of' | translate}} {{$ctrl.contractHolidays.totalHolidays || 0}} {{'days' | translate}}
@@ -55,7 +55,7 @@
order="businessFk DESC"
limit="5">
- ID: {{businessFk}}
+ #{{businessFk}}
{{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}}
diff --git a/modules/worker/front/time-control/index.html b/modules/worker/front/time-control/index.html
index a333b8585..66d6f282e 100644
--- a/modules/worker/front/time-control/index.html
+++ b/modules/worker/front/time-control/index.html
@@ -43,9 +43,16 @@
ng-class="::{'invisible': hour.direction == 'middle'}">
+ on-remove="$ctrl.showDeleteDialog($event, hour)"
+ ng-click="$ctrl.edit($event, hour)"
+ >
+
+
+
+
{{::hour.timed | date: 'HH:mm'}}
@@ -97,10 +104,20 @@
+ required="true">
+
+
@@ -112,4 +129,22 @@
on-accept="$ctrl.deleteTimeEntry()"
message="This time entry will be deleted"
question="Are you sure you want to delete this entry?">
-
\ 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 bed9e7af3..b980243c9 100644
--- a/modules/worker/front/time-control/index.js
+++ b/modules/worker/front/time-control/index.js
@@ -7,6 +7,11 @@ class Controller extends Section {
super($element, $);
this.weekDays = [];
this.weekdayNames = vnWeekDays.locales;
+ this.entryDirections = [
+ {code: 'in', description: this.$t('In')},
+ {code: 'middle', description: this.$t('Intermediate')},
+ {code: 'out', description: this.$t('Out')}
+ ];
}
$postLink() {
@@ -241,21 +246,34 @@ class Controller extends Section {
const timed = new Date(weekday.dated.getTime());
timed.setHours(0, 0, 0, 0);
- this.newTime = timed;
+ this.newTimeEntry = {
+ workerFk: this.$params.id,
+ timed: timed
+ };
this.selectedWeekday = weekday;
this.$.addTimeDialog.show();
}
addTime() {
- let data = {
- workerFk: this.$params.id,
- timed: this.newTime
- };
- this.$http.post(`WorkerTimeControls/addTimeEntry`, data)
- .then(() => this.fetchHours());
+ try {
+ const entry = this.newTimeEntry;
+ if (!entry.direction)
+ throw new Error(`The entry type can't be empty`);
+
+ const query = `WorkerTimeControls/${this.worker.id}/addTimeEntry`;
+ this.$http.post(query, entry)
+ .then(() => this.fetchHours());
+ } catch (e) {
+ this.vnApp.showError(this.$t(e.message));
+ return false;
+ }
+
+ return true;
}
- showDeleteDialog(hour) {
+ showDeleteDialog($event, hour) {
+ $event.preventDefault();
+
this.timeEntryToDelete = hour;
this.$.deleteEntryDialog.show();
}
@@ -268,6 +286,29 @@ class Controller extends Section {
this.vnApp.showSuccess(this.$t('Entry removed'));
});
}
+
+ edit($event, hour) {
+ if ($event.defaultPrevented) return;
+
+ this.selectedRow = hour;
+ this.$.editEntry.show($event);
+ }
+
+ save() {
+ try {
+ const entry = this.selectedRow;
+ if (!entry.direction)
+ throw new Error(`The entry type can't be empty`);
+
+ const query = `WorkerTimeControls/${entry.id}/updateTimeEntry`;
+ this.$http.post(query, {direction: entry.direction})
+ .then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
+ .then(() => this.$.editEntry.hide())
+ .then(() => this.fetchHours());
+ } catch (e) {
+ this.vnApp.showError(this.$t(e.message));
+ }
+ }
}
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
diff --git a/modules/worker/front/time-control/index.spec.js b/modules/worker/front/time-control/index.spec.js
index 4e9730d9c..920f13e7c 100644
--- a/modules/worker/front/time-control/index.spec.js
+++ b/modules/worker/front/time-control/index.spec.js
@@ -113,5 +113,21 @@ describe('Component vnWorkerTimeControl', () => {
expect(result).toEqual('01:00');
});
});
+
+ describe('save() ', () => {
+ it(`should make a query an then call to the fetchHours() method`, () => {
+ controller.fetchHours = jest.fn();
+ controller.selectedRow = {id: 1, timed: new Date(), direction: 'in'};
+ controller.$.editEntry = {
+ hide: () => {}
+ };
+ const expectedParams = {direction: 'in'};
+ $httpBackend.expect('POST', 'WorkerTimeControls/1/updateTimeEntry', expectedParams).respond(200);
+ controller.save();
+ $httpBackend.flush();
+
+ expect(controller.fetchHours).toHaveBeenCalledWith();
+ });
+ });
});
});
diff --git a/modules/worker/front/time-control/locale/es.yml b/modules/worker/front/time-control/locale/es.yml
index 1d28b0e2b..8b2486f05 100644
--- a/modules/worker/front/time-control/locale/es.yml
+++ b/modules/worker/front/time-control/locale/es.yml
@@ -1,5 +1,6 @@
In: Entrada
Out: Salida
+Intermediate: Intermedio
Hour: Hora
Hours: Horas
Add time: Añadir hora
@@ -8,4 +9,5 @@ Current week: Semana actual
This time entry will be deleted: Se eliminará 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
+Entry removed: Fichada borrada
+The entry type can't be empty: El tipo de fichada no puede quedar vacía
\ 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 99a21883f..6a46921a7 100644
--- a/modules/worker/front/time-control/style.scss
+++ b/modules/worker/front/time-control/style.scss
@@ -24,4 +24,8 @@ vn-worker-time-control {
.totalBox {
max-width: none
}
+}
+
+.edit-time-entry {
+ width: 200px
}
\ No newline at end of file