Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2973-module_zone_transactions

This commit is contained in:
Carlos Jimenez Ruiz 2021-06-21 09:08:03 +02:00
commit 48a2bb7b10
32 changed files with 649 additions and 224 deletions

View File

@ -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 ;

View File

@ -837,7 +837,8 @@ export default {
saveButton: 'vn-worker-pbx button[type=submit]' saveButton: 'vn-worker-pbx button[type=submit]'
}, },
workerTimeControl: { 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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"]', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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', 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)', 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)', 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)', wednesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(3)',

View File

@ -22,7 +22,8 @@ describe('Worker time control path', () => {
const scanTime = '07:00'; const scanTime = '07:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText');
@ -33,7 +34,8 @@ describe('Worker time control path', () => {
const scanTime = '10:00'; const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfMonday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfMonday, 'innerText');
@ -44,7 +46,8 @@ describe('Worker time control path', () => {
const scanTime = '18:00'; const scanTime = '18:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText');
@ -66,7 +69,8 @@ describe('Worker time control path', () => {
const scanTime = '14:00'; const scanTime = '14:00';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText');
@ -77,7 +81,8 @@ describe('Worker time control path', () => {
const scanTime = '10:20'; const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfMonday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfMonday, 'innerText');
@ -103,7 +108,8 @@ describe('Worker time control path', () => {
const scanTime = '08:00'; const scanTime = '08:00';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfTuesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfTuesday, 'innerText');
@ -114,7 +120,8 @@ describe('Worker time control path', () => {
const scanTime = '10:00'; const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfTuesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfTuesday, 'innerText');
@ -125,7 +132,8 @@ describe('Worker time control path', () => {
const scanTime = '10:20'; const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfTuesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfTuesday, 'innerText');
@ -136,7 +144,8 @@ describe('Worker time control path', () => {
const scanTime = '16:00'; const scanTime = '16:00';
await page.waitToClick(selectors.workerTimeControl.tuesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfTuesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfTuesday, 'innerText');
@ -153,7 +162,8 @@ describe('Worker time control path', () => {
const scanTime = '09:00'; const scanTime = '09:00';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfWednesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfWednesday, 'innerText');
@ -164,7 +174,8 @@ describe('Worker time control path', () => {
const scanTime = '10:00'; const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfWednesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfWednesday, 'innerText');
@ -175,7 +186,8 @@ describe('Worker time control path', () => {
const scanTime = '10:20'; const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfWednesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfWednesday, 'innerText');
@ -186,7 +198,8 @@ describe('Worker time control path', () => {
const scanTime = '17:00'; const scanTime = '17:00';
await page.waitToClick(selectors.workerTimeControl.wednesdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfWednesday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfWednesday, 'innerText');
@ -203,7 +216,8 @@ describe('Worker time control path', () => {
const scanTime = '09:59'; const scanTime = '09:59';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfThursday, 'innerText'); 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() => { it(`should joyfully scan out Hank Pym for break`, async() => {
const scanTime = '10:00'; const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfThursday, 'innerText'); 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() => { it(`should joyfully scan in Hank Pym from the break`, async() => {
const scanTime = '10:20'; const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfThursday, 'innerText'); 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() => { it(`should joyfully scan out Hank Pym for the day`, async() => {
const scanTime = '17:59'; const scanTime = '17:59';
await page.waitToClick(selectors.workerTimeControl.thursdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfThursday, 'innerText'); 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() => { it('should smilingly scan in Hank Pym', async() => {
const scanTime = '07:30'; const scanTime = '07:30';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfFriday, 'innerText'); 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() => { it(`should smilingly scan out Hank Pym for break`, async() => {
const scanTime = '10:00'; const scanTime = '10:00';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfFriday, 'innerText'); 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() => { it(`should smilingly scan in Hank Pym from the break`, async() => {
const scanTime = '10:20'; const scanTime = '10:20';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfFriday, 'innerText'); 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() => { it(`should smilingly scan out Hank Pym for the day`, async() => {
const scanTime = '15:30'; const scanTime = '15:30';
await page.waitToClick(selectors.workerTimeControl.fridayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfFriday, 'innerText'); 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() => { it('should lovingly scan in Hank Pym', async() => {
const scanTime = '06:00'; 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.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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfSaturday, 'innerText'); 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() => { it(`should lovingly scan out Hank Pym for the day with no break to leave a bit early`, async() => {
const scanTime = '13:40'; const scanTime = '13:40';
await page.waitToClick(selectors.workerTimeControl.saturdayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfSaturday, 'innerText'); 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() => { it('should gladly scan in Hank Pym', async() => {
const scanTime = '05:00'; const scanTime = '05:00';
await page.waitToClick(selectors.workerTimeControl.sundayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfSunday, 'innerText'); 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() => { it(`should gladly scan out Hank Pym for the day with no break to leave a bit early`, async() => {
const scanTime = '12:40'; const scanTime = '12:40';
await page.waitToClick(selectors.workerTimeControl.sundayAddTimeButton); 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'); await page.respondToDialog('accept');
const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfSunday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.secondEntryOfSunday, 'innerText');

View File

@ -65,6 +65,7 @@ describe('Account create and basic data path', () => {
describe('Descriptor option', () => { describe('Descriptor option', () => {
describe('Edit role', () => { describe('Edit role', () => {
it('should edit the role using the descriptor menu', async() => { 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.menuButton);
await page.waitToClick(selectors.accountDescriptor.changeRole); await page.waitToClick(selectors.accountDescriptor.changeRole);
await page.autocompleteSearch(selectors.accountDescriptor.newRole, 'adminBoss'); await page.autocompleteSearch(selectors.accountDescriptor.newRole, 'adminBoss');

View File

@ -1,6 +1,12 @@
<div
ng-transclude="prepend"
class="prepend"></div>
<div ng-transclude></div> <div ng-transclude></div>
<div
ng-transclude="append"
class="append"></div>
<vn-icon <vn-icon
ng-click="$ctrl.onRemove()" ng-click="$ctrl.onRemove($event)"
ng-if="$ctrl.removable" ng-if="$ctrl.removable"
icon="cancel" icon="cancel"
tabindex="0"> tabindex="0">

View File

@ -3,16 +3,19 @@ import Component from '../../lib/component';
import './style.scss'; import './style.scss';
export default class Chip extends Component { export default class Chip extends Component {
onRemove() { onRemove($event) {
if (!this.disabled) this.emit('remove'); if (!this.disabled) this.emit('remove', {$event});
} }
} }
Chip.$inject = ['$element', '$scope', '$transclude']; Chip.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnChip', { ngModule.vnComponent('vnChip', {
template: require('./index.html'), template: require('./index.html'),
transclude: {
prepend: '?prepend',
append: '?append'
},
controller: Chip, controller: Chip,
transclude: true,
bindings: { bindings: {
disabled: '<?', disabled: '<?',
removable: '<?' removable: '<?'

View File

@ -18,9 +18,14 @@ describe('Component vnChip', () => {
it(`should emit remove event`, () => { it(`should emit remove event`, () => {
controller.emit = () => {}; controller.emit = () => {};
jest.spyOn(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});
}); });
}); });
}); });

View File

@ -1,4 +1,5 @@
@import "variables"; @import "variables";
@import "effects";
vn-chip { vn-chip {
border-radius: 16px; border-radius: 16px;
@ -24,25 +25,47 @@ vn-chip {
&.transparent { &.transparent {
background-color: transparent; background-color: transparent;
} }
&.colored { &.colored,
&.colored.clickable:hover,
&.colored.clickable:focus {
background-color: $color-main; background-color: $color-main;
color: $color-font-bg; 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; background-color: $color-success-medium;
} }
&.warning { &.warning,
&.warning.clickable:hover,
&.warning.clickable:focus {
background-color: $color-main-medium; background-color: $color-main-medium;
} }
&.alert { &.alert,
&.alert.clickable:hover,
&.alert.clickable:focus {
background-color: $color-alert-medium; background-color: $color-alert-medium;
} }
&.message { &.message,
&.message.clickable:hover,
&.message.clickable:focus {
color: $color-font-dark; 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 { & > div {
@ -75,6 +98,20 @@ vn-chip {
opacity: 1; opacity: 1;
} }
} }
& > .prepend {
padding: 0 5px;
padding-right: 0;
&:empty {display:none;}
}
& > .append {
padding: 0 5px;
padding-left: 0;
&:empty {display:none;}
}
} }
vn-avatar { vn-avatar {

View File

@ -97,5 +97,6 @@
"Role name must be written in camelCase": "Role name must be written in camelCase", "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}}})", "Client assignment has changed": "I did change the salesperson ~*\"<{{previousWorkerName}}>\"*~ by *\"<{{currentWorkerName}}>\"* from the client [{{clientName}} ({{clientId}})]({{{url}}})",
"None": "None", "None": "None",
"error densidad = 0": "error densidad = 0" "error densidad = 0": "error densidad = 0",
"nickname": "nickname"
} }

View File

@ -1,11 +1,12 @@
<vn-crud-model <vn-crud-model
vn-id="ticketsModel" vn-id="ticketsModel"
auto-load="true" auto-load="true"
url="Tickets/filter" url="Tickets"
link="{'t.clientFk': $ctrl.$params.id}" link="{clientFk: $ctrl.$params.id}"
filter="::$ctrl.ticketFilter"
limit="5" limit="5"
data="tickets" data="tickets"
order="shippedDate DESC, shippedHour ASC"> order="shipped DESC">
</vn-crud-model> </vn-crud-model>
<vn-card class="summary"> <vn-card class="summary">
<h5> <h5>
@ -292,9 +293,9 @@
<vn-tr> <vn-tr>
<vn-th field="id" number>Id</vn-th> <vn-th field="id" number>Id</vn-th>
<vn-th field="nickname" expand>Client</vn-th> <vn-th field="nickname" expand>Client</vn-th>
<vn-th field="salesPersonFk">Salesperson</vn-th> <vn-th field="packages" shrink>Packages</vn-th>
<vn-th field="shipped" shrink-date>Date</vn-th> <vn-th field="shipped" shrink-date>Date</vn-th>
<vn-th field="stateFk">State</vn-th> <vn-th>State</vn-th>
<vn-th shrink>Total</vn-th> <vn-th shrink>Total</vn-th>
<vn-th></vn-th> <vn-th></vn-th>
</vn-tr> </vn-tr>
@ -312,13 +313,8 @@
{{::ticket.nickname}} {{::ticket.nickname}}
</span> </span>
</vn-td> </vn-td>
<vn-td> <vn-td shrink>
<span {{::ticket.packages}}
title="{{::ticket.userName}}"
vn-click-stop="workerDescriptor.show($event, ticket.salesPersonFk)"
class="link">
{{::ticket.userName | dashIfEmpty}}
</span>
</vn-td> </vn-td>
<vn-td shrink-date> <vn-td shrink-date>
<span class="chip {{::$ctrl.chipColor(ticket.shipped)}}"> <span class="chip {{::$ctrl.chipColor(ticket.shipped)}}">
@ -336,7 +332,7 @@
<span <span
ng-show="::!ticket.refFk" ng-show="::!ticket.refFk"
class="chip {{::$ctrl.stateColor(ticket)}}"> class="chip {{::$ctrl.stateColor(ticket)}}">
{{::ticket.state}} {{::ticket.ticketState.state.name}}
</span> </span>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>

View File

@ -3,6 +3,21 @@ import Summary from 'salix/components/summary';
import './style.scss'; import './style.scss';
class Controller extends Summary { class Controller extends Summary {
constructor($element, $) {
super($element, $);
this.ticketFilter = {
include: {
relation: 'ticketState',
scope: {
fields: ['stateFk', 'code', 'alertLevel'],
include: {
relation: 'state'
}
}
}
};
}
$onChanges() { $onChanges() {
if (!this.client) if (!this.client)
return; return;
@ -18,6 +33,7 @@ class Controller extends Summary {
} }
}); });
} }
get isEmployee() { get isEmployee() {
return this.aclService.hasAny(['employee']); return this.aclService.hasAny(['employee']);
} }
@ -41,13 +57,15 @@ class Controller extends Summary {
} }
stateColor(ticket) { stateColor(ticket) {
if (ticket.alertLevelCode === 'OK') const ticketState = ticket.ticketState;
if (ticketState.code === 'OK')
return 'success'; return 'success';
else if (ticket.alertLevelCode === 'FREE') else if (ticketState.code === 'FREE')
return 'notice'; return 'notice';
else if (ticket.alertLevel === 1) else if (ticketState.alertLevel === 1)
return 'warning'; return 'warning';
else if (ticket.alertLevel === 0) else if (ticketState.alertLevel === 0)
return 'alert'; return 'alert';
} }

View File

@ -76,25 +76,25 @@ describe('Client', () => {
describe('stateColor()', () => { describe('stateColor()', () => {
it('should return "success" when the alertLevelCode property is "OK"', () => { 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'); expect(result).toEqual('success');
}); });
it('should return "notice" when the alertLevelCode property is "FREE"', () => { 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'); expect(result).toEqual('notice');
}); });
it('should return "warning" when the alertLevel property is "1', () => { 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'); expect(result).toEqual('warning');
}); });
it('should return "alert" when the alertLevel property is "0"', () => { 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'); expect(result).toEqual('alert');
}); });

View File

@ -109,8 +109,9 @@ module.exports = Self => {
return {'ii.created': {gte: value}}; return {'ii.created': {gte: value}};
case 'to': case 'to':
return {'ii.created': {lte: value}}; return {'ii.created': {lte: value}};
case 'account':
case 'fi': case 'fi':
return {'s.nif': value};
case 'account':
return {[`s.${param}`]: value}; return {[`s.${param}`]: value};
case 'supplierRef': case 'supplierRef':
case 'supplierFk': case 'supplierFk':

View File

@ -54,19 +54,22 @@
</vn-icon-button> </vn-icon-button>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
<vn-icon-button <!-- <vn-icon-button
ng-show="invoiceIn.dmsFk" ng-show="invoiceIn.dmsFk"
vn-click-stop="$ctrl.openPdf(invoiceIn.dmsFk)" vn-click-stop="$ctrl.openPdf(invoiceIn.dmsFk)"
icon="cloud_download" icon="cloud_download"
title="Download PDF" title="Download PDF"
vn-tooltip="Download PDF"> vn-tooltip="Download PDF">
</vn-icon-button> </vn-icon-button> -->
</vn-td> </vn-td>
</a> </a>
</vn-tbody> </vn-tbody>
</vn-table> </vn-table>
</vn-card> </vn-card>
</vn-data-viewer> </vn-data-viewer>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<vn-popup vn-id="summary"> <vn-popup vn-id="summary">
<vn-invoice-in-summary <vn-invoice-in-summary
invoice-in="$ctrl.selectedInvoiceIn"> invoice-in="$ctrl.selectedInvoiceIn">

View File

@ -27,11 +27,12 @@
label="Account" label="Account"
ng-model="filter.account"> ng-model="filter.account">
</vn-textfield> </vn-textfield>
<vn-textfield <vn-input-number
vn-one vn-one
label="Amount" label="Amount"
ng-model="filter.amount"> ng-model="filter.amount"
</vn-textfield> step="0.01">
</vn-input-number>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-date-picker <vn-date-picker

View File

@ -36,7 +36,7 @@
<vn-td expand>{{::invoiceOut.created | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td> <vn-td expand>{{::invoiceOut.created | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
<vn-td>{{::invoiceOut.companyCode | dashIfEmpty}}</vn-td> <vn-td>{{::invoiceOut.companyCode | dashIfEmpty}}</vn-td>
<vn-td shrink>{{::invoiceOut.dued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td> <vn-td shrink>{{::invoiceOut.dued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
<vn-td> <vn-td shrink>
<vn-icon-button <vn-icon-button
ng-show="invoiceOut.hasPdf" ng-show="invoiceOut.hasPdf"
vn-click-stop="$ctrl.openPdf(invoiceOut.id)" vn-click-stop="$ctrl.openPdf(invoiceOut.id)"
@ -45,7 +45,7 @@
vn-tooltip="Download PDF"> vn-tooltip="Download PDF">
</vn-icon-button> </vn-icon-button>
</vn-td> </vn-td>
<vn-td> <vn-td shrink>
<vn-icon-button <vn-icon-button
vn-click-stop="$ctrl.preview(invoiceOut)" vn-click-stop="$ctrl.preview(invoiceOut)"
vn-tooltip="Preview" vn-tooltip="Preview"

View File

@ -19,6 +19,11 @@ module.exports = Self => {
description: 'The client id', description: 'The client id',
required: true required: true
}, },
{
arg: 'nickname',
type: 'string',
description: 'The client nickname'
},
{ {
arg: 'agencyModeFk', arg: 'agencyModeFk',
type: 'number', type: 'number',
@ -145,10 +150,11 @@ module.exports = Self => {
// Force to unroute ticket // Force to unroute ticket
const hasToBeUnrouted = true; const hasToBeUnrouted = true;
const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
const res = await Self.rawSql(query, [ const res = await Self.rawSql(query, [
args.id, args.id,
args.clientFk, args.clientFk,
args.nickname,
args.agencyModeFk, args.agencyModeFk,
args.addressFk, args.addressFk,
args.zoneFk, args.zoneFk,

View File

@ -20,6 +20,16 @@
initial-data="$ctrl.clientId" initial-data="$ctrl.clientId"
order="id"> order="id">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one
required="true"
url="Warehouses"
label="Warehouse"
show-field="name"
value-field="id"
ng-model="$ctrl.warehouseId">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete <vn-autocomplete
vn-one vn-one
required="true" required="true"
@ -44,14 +54,12 @@
</vn-icon-button> </vn-icon-button>
</append> </append>
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-textfield
vn-one
label="Alias"
required="true" required="true"
url="Warehouses" ng-model="$ctrl.ticket.nickname">
label="Warehouse" </vn-textfield>
show-field="name"
value-field="id"
ng-model="$ctrl.warehouseId">
</vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-autocomplete <vn-autocomplete

View File

@ -40,6 +40,7 @@ class Controller extends Component {
set addressId(value) { set addressId(value) {
if (value != this.ticket.addressFk) { if (value != this.ticket.addressFk) {
this.ticket.addressFk = value; this.ticket.addressFk = value;
this.onChangeAddress(value);
this.getShipped({ this.getShipped({
landed: this.ticket.landed, landed: this.ticket.landed,
addressFk: value, addressFk: value,
@ -179,6 +180,14 @@ class Controller extends Component {
}); });
} }
onChangeAddress(addressId) {
this.ticket.nickname = null;
const query = `Addresses/${addressId}`;
this.$http.get(query).then(res => {
this.ticket.nickname = res.data.nickname;
});
}
async onStepChange() { async onStepChange() {
if (this.isFormInvalid()) { if (this.isFormInvalid()) {
return this.vnApp.showError( return this.vnApp.showError(

View File

@ -71,6 +71,7 @@ class Controller extends Component {
let query = `tickets/${this.ticket.id}/componentUpdate`; let query = `tickets/${this.ticket.id}/componentUpdate`;
let params = { let params = {
clientFk: this.ticket.clientFk, clientFk: this.ticket.clientFk,
nickname: this.ticket.nickname,
agencyModeFk: this.ticket.agencyModeFk, agencyModeFk: this.ticket.agencyModeFk,
addressFk: this.ticket.addressFk, addressFk: this.ticket.addressFk,
zoneFk: this.ticket.zoneFk, zoneFk: this.ticket.zoneFk,

View File

@ -5,38 +5,54 @@ module.exports = Self => {
description: 'Adds a new hour registry', description: 'Adds a new hour registry',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'data', arg: 'id',
type: 'object', type: 'number',
required: true, description: 'The worker id',
description: 'workerFk, timed', http: {source: 'path'}
http: {source: 'body'} },
{
arg: 'timed',
type: 'date',
required: true
},
{
arg: 'direction',
type: 'string',
required: true
}], }],
returns: [{ returns: [{
type: 'Object', type: 'Object',
root: true root: true
}], }],
http: { http: {
path: `/addTimeEntry`, path: `/:id/addTimeEntry`,
verb: 'POST' verb: 'POST'
} }
}); });
Self.addTimeEntry = async(ctx, data) => { Self.addTimeEntry = async(ctx, workerId, options) => {
const Worker = Self.app.models.Worker; const models = Self.app.models;
const myUserId = ctx.req.accessToken.userId; const args = ctx.args;
const myWorker = await Worker.findOne({where: {userFk: myUserId}}); const currentUserId = ctx.req.accessToken.userId;
const isSubordinate = await Worker.isSubordinate(ctx, data.workerFk);
const isTeamBoss = await Self.app.models.Account.hasRole(myUserId, 'teamBoss');
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`); throw new UserError(`You don't have enough privileges`);
const subordinate = await Worker.findById(data.workerFk); const timed = new Date(args.timed);
const timed = new Date(data.timed);
let [result] = await Self.rawSql('SELECT vn.workerTimeControl_add(?, ?, ?, ?) AS id', [ return models.WorkerTimeControl.create({
subordinate.userFk, null, timed, true]); userFk: workerId,
direction: args.direction,
return result; timed: timed,
manual: true
}, myOptions);
}; };
}; };

View File

@ -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 currentUserId = ctx.req.accessToken.userId;
const workerModel = Self.app.models.Worker; const models = Self.app.models;
const targetTimeEntry = await Self.findById(id); let myOptions = {};
const isSubordinate = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk);
const isTeamBoss = await Self.app.models.Account.hasRole(currentUserId, 'teamBoss'); 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 isHimself = currentUserId == targetTimeEntry.userFk;
const notAllowed = isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss); if (isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss))
if (notAllowed)
throw new UserError(`You don't have enough privileges`); throw new UserError(`You don't have enough privileges`);
return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [ return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [
targetTimeEntry.userFk, targetTimeEntry.timed]); targetTimeEntry.userFk, targetTimeEntry.timed], myOptions);
}; };
}; };

View File

@ -1,5 +1,6 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
const models = app.models;
describe('workerTimeControl add/delete timeEntry()', () => { describe('workerTimeControl add/delete timeEntry()', () => {
const HHRRId = 37; const HHRRId = 37;
@ -12,19 +13,6 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}; };
let ctx = {req: activeCtx}; 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(() => { beforeAll(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx 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() => { it('should fail to add a time entry if the target user is not a subordinate', async() => {
activeCtx.accessToken.userId = employeeId; activeCtx.accessToken.userId = employeeId;
const workerId = 2;
let error; let error;
let data = {
workerFk: 2,
timed: new Date()
};
try { try {
await app.models.WorkerTimeControl.addTimeEntry(ctx, data); ctx.args = {timed: new Date(), direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId);
} catch (e) { } catch (e) {
error = 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() => { 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; activeCtx.accessToken.userId = employeeId;
const workerId = employeeId;
let error; let error;
let data = {
workerFk: 1,
timed: new Date()
};
try { try {
await app.models.WorkerTimeControl.addTimeEntry(ctx, data); ctx.args = {timed: new Date(), direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId);
} catch (e) { } catch (e) {
error = 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() => { it('should add if the current user is team boss and the target user is a himself', async() => {
activeCtx.accessToken.userId = teamBossId; activeCtx.accessToken.userId = teamBossId;
let todayAtSix = new Date(); const workerId = teamBossId;
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
const todayAtSix = new Date();
todayAtSix.setHours(18, 30, 0, 0); todayAtSix.setHours(18, 30, 0, 0);
let data = { ctx.args = {timed: todayAtSix, direction: 'in'};
workerFk: teamBossId, const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
timed: todayAtSix
};
timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data); expect(createdTimeEntry.id).toBeDefined();
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id); await tx.rollback();
} catch (e) {
expect(createdTimeEntry).toBeDefined(); await tx.rollback();
throw e;
}
}); });
it('should try but fail to delete his own time entry', async() => { it('should try but fail to delete his own time entry', async() => {
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = salesBossId;
let error; let error;
let todayAtSeven = new Date(); const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
const todayAtSeven = new Date();
todayAtSeven.setHours(19, 30, 0, 0); todayAtSeven.setHours(19, 30, 0, 0);
let data = { ctx.args = {timed: todayAtSeven, direction: 'in'};
workerFk: salesPersonId, const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
timed: todayAtSeven
};
timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data);
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id);
try {
activeCtx.accessToken.userId = salesPersonId; 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) { } catch (e) {
error = e; error = e;
await tx.rollback();
} }
expect(error).toBeDefined(); 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() => { it('should delete the created time entry for the team boss as himself', async() => {
activeCtx.accessToken.userId = teamBossId; activeCtx.accessToken.userId = teamBossId;
const workerId = teamBossId;
let todayAtFive = new Date(); const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
const todayAtFive = new Date();
todayAtFive.setHours(17, 30, 0, 0); todayAtFive.setHours(17, 30, 0, 0);
let data = { ctx.args = {timed: todayAtFive, direction: 'in'};
workerFk: teamBossId, const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
timed: todayAtFive
};
timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data); expect(createdTimeEntry.id).toBeDefined();
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id); await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
expect(createdTimeEntry).toBeDefined(); const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id); expect(deletedTimeEntry).toBeNull();
await tx.rollback();
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id); } catch (e) {
await tx.rollback();
expect(createdTimeEntry).toBeNull(); throw e;
}
}); });
it('should delete the created time entry for the team boss as HHRR', async() => { it('should delete the created time entry for the team boss as HHRR', async() => {
activeCtx.accessToken.userId = HHRRId; activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
let todayAtFive = new Date(); const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
const todayAtFive = new Date();
todayAtFive.setHours(17, 30, 0, 0); todayAtFive.setHours(17, 30, 0, 0);
let data = { ctx.args = {timed: todayAtFive, direction: 'in'};
workerFk: teamBossId, const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
timed: todayAtFive
};
timeEntry = await app.models.WorkerTimeControl.addTimeEntry(ctx, data); expect(createdTimeEntry.id).toBeDefined();
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id); await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
expect(createdTimeEntry).toBeDefined(); const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
await app.models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id); expect(deletedTimeEntry).toBeNull();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
createdTimeEntry = await app.models.WorkerTimeControl.findById(timeEntry.id); it('should edit the created time entry for the team boss as HHRR', async() => {
activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
expect(createdTimeEntry).toBeNull(); 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;
}
}); });
}); });

View File

@ -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);
};
};

View File

@ -4,6 +4,7 @@ module.exports = Self => {
require('../methods/worker-time-control/filter')(Self); require('../methods/worker-time-control/filter')(Self);
require('../methods/worker-time-control/addTimeEntry')(Self); require('../methods/worker-time-control/addTimeEntry')(Self);
require('../methods/worker-time-control/deleteTimeEntry')(Self); require('../methods/worker-time-control/deleteTimeEntry')(Self);
require('../methods/worker-time-control/updateTimeEntry')(Self);
Self.rewriteDbError(function(err) { Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY') if (err.code === 'ER_DUP_ENTRY')

View File

@ -9,16 +9,16 @@
"properties": { "properties": {
"id": { "id": {
"id": true, "id": true,
"type": "Number" "type": "number"
}, },
"timed": { "timed": {
"type": "Date" "type": "date"
}, },
"manual": { "manual": {
"type": "Boolean" "type": "boolean"
}, },
"order": { "order": {
"type": "Number" "type": "number"
}, },
"direction": { "direction": {
"type": "string" "type": "string"

View File

@ -23,7 +23,7 @@
<vn-side-menu side="right"> <vn-side-menu side="right">
<div class="vn-pa-md"> <div class="vn-pa-md">
<div class="totalBox vn-mb-sm" style="text-align: center;"> <div class="totalBox vn-mb-sm" style="text-align: center;">
<h6>{{'Contract' | translate}} ID: {{$ctrl.businessId}}</h6> <h6>{{'Contract' | translate}} #{{$ctrl.businessId}}</h6>
<div> <div>
{{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed}} {{'Used' | translate}} {{$ctrl.contractHolidays.holidaysEnjoyed}}
{{'of' | translate}} {{$ctrl.contractHolidays.totalHolidays || 0}} {{'days' | translate}} {{'of' | translate}} {{$ctrl.contractHolidays.totalHolidays || 0}} {{'days' | translate}}
@ -55,7 +55,7 @@
order="businessFk DESC" order="businessFk DESC"
limit="5"> limit="5">
<tpl-item> <tpl-item>
<div>ID: {{businessFk}}</div> <div>#{{businessFk}}</div>
<div class="text-caption text-secondary"> <div class="text-caption text-secondary">
{{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}} {{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}}
</div> </div>

View File

@ -43,9 +43,16 @@
ng-class="::{'invisible': hour.direction == 'middle'}"> ng-class="::{'invisible': hour.direction == 'middle'}">
</vn-icon> </vn-icon>
<vn-chip <vn-chip
ng-class="::{'colored': hour.manual}" ng-class="::{'colored': hour.manual, 'clickable': true}"
removable="::hour.manual" removable="::hour.manual"
on-remove="$ctrl.showDeleteDialog(hour)"> on-remove="$ctrl.showDeleteDialog($event, hour)"
ng-click="$ctrl.edit($event, hour)"
>
<prepend>
<vn-icon icon="edit"
vn-tooltip="Edit">
</vn-icon>
</prepend>
{{::hour.timed | date: 'HH:mm'}} {{::hour.timed | date: 'HH:mm'}}
</vn-chip> </vn-chip>
</section> </section>
@ -97,10 +104,20 @@
<tpl-body> <tpl-body>
<vn-input-time <vn-input-time
vn-one vn-one
ng-model="$ctrl.newTime" vn-focus
ng-model="$ctrl.newTimeEntry.timed"
label="Hour" label="Hour"
vn-focus> required="true">
</vn-input-time> </vn-input-time>
<vn-autocomplete
label="Type"
ng-model="$ctrl.newTimeEntry.direction"
data="$ctrl.entryDirections"
select-fields="['code','description']"
show-field="description"
value-field="code"
required="true">
</vn-autocomplete>
</tpl-body> </tpl-body>
<tpl-buttons> <tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/> <input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
@ -113,3 +130,21 @@
message="This time entry will be deleted" message="This time entry will be deleted"
question="Are you sure you want to delete this entry?"> question="Are you sure you want to delete this entry?">
</vn-confirm> </vn-confirm>
<!-- Edit entry Popover -->
<vn-popover vn-id="editEntry">
<vn-horizontal class="vn-pa-sm edit-time-entry">
<vn-autocomplete class="dense"
ng-model="$ctrl.selectedRow.direction"
data="$ctrl.entryDirections"
select-fields="['code','description']"
show-field="description"
value-field="code">
</vn-autocomplete>
<vn-icon-button vn-none
icon="check"
vn-tooltip="Save"
ng-click="$ctrl.save()">
</vn-icon-button>
</vn-horizontal>
</vn-popover>

View File

@ -7,6 +7,11 @@ class Controller extends Section {
super($element, $); super($element, $);
this.weekDays = []; this.weekDays = [];
this.weekdayNames = vnWeekDays.locales; 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() { $postLink() {
@ -241,21 +246,34 @@ class Controller extends Section {
const timed = new Date(weekday.dated.getTime()); const timed = new Date(weekday.dated.getTime());
timed.setHours(0, 0, 0, 0); timed.setHours(0, 0, 0, 0);
this.newTime = timed; this.newTimeEntry = {
workerFk: this.$params.id,
timed: timed
};
this.selectedWeekday = weekday; this.selectedWeekday = weekday;
this.$.addTimeDialog.show(); this.$.addTimeDialog.show();
} }
addTime() { addTime() {
let data = { try {
workerFk: this.$params.id, const entry = this.newTimeEntry;
timed: this.newTime if (!entry.direction)
}; throw new Error(`The entry type can't be empty`);
this.$http.post(`WorkerTimeControls/addTimeEntry`, data)
const query = `WorkerTimeControls/${this.worker.id}/addTimeEntry`;
this.$http.post(query, entry)
.then(() => this.fetchHours()); .then(() => this.fetchHours());
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
} }
showDeleteDialog(hour) { return true;
}
showDeleteDialog($event, hour) {
$event.preventDefault();
this.timeEntryToDelete = hour; this.timeEntryToDelete = hour;
this.$.deleteEntryDialog.show(); this.$.deleteEntryDialog.show();
} }
@ -268,6 +286,29 @@ class Controller extends Section {
this.vnApp.showSuccess(this.$t('Entry removed')); 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']; Controller.$inject = ['$element', '$scope', 'vnWeekDays'];

View File

@ -113,5 +113,21 @@ describe('Component vnWorkerTimeControl', () => {
expect(result).toEqual('01:00'); 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();
});
});
}); });
}); });

View File

@ -1,5 +1,6 @@
In: Entrada In: Entrada
Out: Salida Out: Salida
Intermediate: Intermedio
Hour: Hora Hour: Hora
Hours: Horas Hours: Horas
Add time: Añadir hora Add time: Añadir hora
@ -9,3 +10,4 @@ This time entry will be deleted: Se eliminará la hora fichada
Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla? Are you sure you want to delete this entry?: ¿Seguro que quieres eliminarla?
Finish at: Termina a las Finish at: Termina a las
Entry removed: Fichada borrada Entry removed: Fichada borrada
The entry type can't be empty: El tipo de fichada no puede quedar vacía

View File

@ -25,3 +25,7 @@ vn-worker-time-control {
max-width: none max-width: none
} }
} }
.edit-time-entry {
width: 200px
}