#1852 worker.time-control

This commit is contained in:
Carlos Jimenez Ruiz 2019-11-14 14:20:05 +01:00
parent d12f815c80
commit 34bd35ce4e
5 changed files with 113 additions and 63 deletions

View File

@ -1833,12 +1833,12 @@ INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `from`)
(8, DATE_ADD(CURDATE(), INTERVAL +5 DAY)),
(8, DATE_ADD(CURDATE(), INTERVAL +6 DAY));
INSERT INTO `vn`.`workerTimeControl`(`userFk`,`timed`,`manual`)
INSERT INTO `vn`.`workerTimeControl`(`userFk`,`timed`,`manual`, `direction`)
VALUES
(106, CONCAT(CURDATE(), ' 07:00'), TRUE),
(106, CONCAT(CURDATE(), ' 10:00'), TRUE),
(106, CONCAT(CURDATE(), ' 10:10'), TRUE),
(106, CONCAT(CURDATE(), ' 15:00'), TRUE);
(106, CONCAT(CURDATE(), ' 07:00'), TRUE, 'in'),
(106, CONCAT(CURDATE(), ' 10:00'), TRUE, 'middle'),
(106, CONCAT(CURDATE(), ' 10:10'), TRUE, 'middle'),
(106, CONCAT(CURDATE(), ' 15:00'), TRUE, 'out');
INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `code`)
VALUES
@ -1950,3 +1950,8 @@ INSERT INTO `vn`.`userPhone`(`id`, `userFk`, `typeFk`, `phone`)
(65, 107, 'businessPhone', 700987987),
(67, 106, 'businessPhone', 1111111112),
(68, 106, 'personalPhone', 1111111113);
INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `weekScope`, `dayWorkMax`, `dayStayMax`)
VALUES
(1, 43200, 129600, 734400, 43200, 50400);

View File

@ -679,34 +679,35 @@ export default {
saturdayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(6) > vn-icon-button',
sundayAddTimeButton: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(2) > vn-td:nth-child(7) > vn-icon-button',
confirmButton: '.vn-dialog.shown tpl-buttons > button',
firstEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > span',
firstEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > span',
firstEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > span',
firstEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > span',
firstEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > span',
firstEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > span',
firstEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > span',
secondEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > span',
secondEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > span',
secondEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > span',
secondEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > span',
secondEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > span',
secondEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > span',
secondEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > span',
thirdEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > span',
thirdEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > span',
thirdEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > span',
thirdEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > span',
thirdEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > span',
thirdEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > span',
thirdEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > span',
fourthEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > span',
fourthEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > span',
fourthEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > span',
fourthEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > span',
fourthEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > span',
fourthEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > span',
fourthEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > span',
firstEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(1) > vn-chip > div',
firstEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(1) > vn-chip > div',
firstEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(1) > vn-chip > div',
firstEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(1) > vn-chip > div',
firstEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(1) > vn-chip > div',
firstEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(1) > vn-chip > div',
firstEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(1) > vn-chip > div',
secondEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(2) > vn-chip > div',
secondEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(2) > vn-chip > div',
secondEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(2) > vn-chip > div',
secondEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(2) > vn-chip > div',
secondEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(2) > vn-chip > div',
secondEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(2) > vn-chip > div',
secondEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(2) > vn-chip > div',
thirdEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > vn-chip > div',
thirdEntryOfMondayDelete: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(3) > vn-chip > vn-icon[icon="cancel"]',
thirdEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(3) > vn-chip > div',
thirdEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(3) > vn-chip > div',
thirdEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(3) > vn-chip > div',
thirdEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(3) > vn-chip > div',
thirdEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(3) > vn-chip > div',
thirdEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(3) > vn-chip > div',
fourthEntryOfMonday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(1) > section:nth-child(4) > vn-chip > div',
fourthEntryOfTuesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(2) > section:nth-child(4) > vn-chip > div',
fourthEntryOfWednesday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(3) > section:nth-child(4) > vn-chip > div',
fourthEntryOfThursday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4) > section:nth-child(4) > vn-chip > div',
fourthEntryOfFriday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5) > section:nth-child(4) > vn-chip > div',
fourthEntryOfSaturday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(6) > section:nth-child(4) > vn-chip > div',
fourthEntryOfSunday: 'vn-worker-time-control vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7) > section:nth-child(4) > vn-chip > div',
mondayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(1)',
tuesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(2)',
wednesdayWorkedHours: 'vn-worker-time-control vn-table > div > vn-tfoot > vn-tr:nth-child(1) > vn-td:nth-child(3)',
@ -717,7 +718,8 @@ export default {
weekWorkedHours: 'vn-worker-time-control vn-side-menu vn-label-value > section > span',
nextMonthButton: 'vn-worker-time-control vn-side-menu vn-calendar vn-button[icon=keyboard_arrow_right]',
secondWeekDay: 'vn-worker-time-control vn-side-menu vn-calendar .day:nth-child(8) > .day-number',
navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]'
navigateBackToIndex: 'vn-worker-descriptor vn-icon[icon="chevron_left"]',
acceptDeleteDialog: '.vn-confirm.shown button[response="accept"]'
},
invoiceOutIndex: {
searchInvoiceOutInput: `vn-invoice-out-index vn-textfield input`,

View File

@ -35,8 +35,30 @@ describe('Worker time control path', () => {
expect(result).toEqual(scanTime);
});
it(`should scan out Hank Pym and forget to scan in from the break`, async() => {
const scanTime = '15:00';
it(`should scan in Hank Pym for a wrong hour and forget to scan in from the break`, async() => {
const scanTime = '18:00';
const result = await nightmare
.waitToClick(selectors.workerTimeControl.mondayAddTimeButton)
.pickTime(selectors.workerTimeControl.timeDialogInput, scanTime)
.waitToClick(selectors.workerTimeControl.confirmButton)
.waitToGetProperty(selectors.workerTimeControl.thirdEntryOfMonday, 'innerText');
expect(result).toEqual(scanTime);
});
it(`should delete the wrong entry for Hank Pym`, async() => {
const wrongScanTime = '18:00';
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.thirdEntryOfMonday, wrongScanTime)
.waitToClick(selectors.workerTimeControl.thirdEntryOfMondayDelete)
.waitToClick(selectors.workerTimeControl.acceptDeleteDialog)
.waitForLastSnackbar();
expect(result).toEqual('Entry removed');
});
it(`should scan out Hank Pym to leave early`, async() => {
const scanTime = '14:00';
const result = await nightmare
.waitToClick(selectors.workerTimeControl.mondayAddTimeButton)
.pickTime(selectors.workerTimeControl.timeDialogInput, scanTime)
@ -54,7 +76,7 @@ describe('Worker time control path', () => {
.waitToClick(selectors.workerTimeControl.confirmButton)
.waitToGetProperty(selectors.workerTimeControl.fourthEntryOfMonday, 'innerText');
expect(result).toEqual('15:00');
expect(result).toEqual('14:00');
});
it(`should the third entry be the scan in from break`, async() => {
@ -67,9 +89,10 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 hours`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '07:00 h.')
.waitToGetProperty(selectors.workerTimeControl.mondayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
expect(result).toEqual('07:00 h.');
});
});
@ -120,6 +143,7 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 happy hours`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.tuesdayWorkedHours, '08:00 h.')
.waitToGetProperty(selectors.workerTimeControl.tuesdayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
@ -173,6 +197,7 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 cheerfull hours`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.wednesdayWorkedHours, '08:00 h.')
.waitToGetProperty(selectors.workerTimeControl.wednesdayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
@ -226,6 +251,7 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 joyfull hours`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.thursdayWorkedHours, '08:00 h.')
.waitToGetProperty(selectors.workerTimeControl.thursdayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
@ -279,6 +305,7 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 hours with a smile on his face`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.fridayWorkedHours, '08:00 h.')
.waitToGetProperty(selectors.workerTimeControl.fridayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
@ -319,6 +346,7 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 hours with all his will`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.saturdayWorkedHours, '08:00 h.')
.waitToGetProperty(selectors.workerTimeControl.saturdayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
@ -350,6 +378,7 @@ describe('Worker time control path', () => {
it(`should check Hank Pym worked 8 glad hours`, async() => {
const result = await nightmare
.waitForTextInElement(selectors.workerTimeControl.sundayWorkedHours, '08:00 h.')
.waitToGetProperty(selectors.workerTimeControl.sundayWorkedHours, 'innerText');
expect(result).toEqual('08:00 h.');
@ -387,10 +416,10 @@ describe('Worker time control path', () => {
it('should Hank Pym check his hours are alright', async() => {
const wholeWeekHours = await nightmare
.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '56:00 h.')
.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '55:00 h.')
.waitToGetProperty(selectors.workerTimeControl.weekWorkedHours, 'innerText');
expect(wholeWeekHours).toEqual('56:00 h.');
expect(wholeWeekHours).toEqual('55:00 h.');
});
});
});

View File

@ -1,3 +1,5 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('deleteTimeEntry', {
description: 'Deletes a manual time entry for a worker if the user role is above the worker',
@ -20,13 +22,17 @@ module.exports = Self => {
});
Self.deleteTimeEntry = async(ctx, id) => {
const currentUserId = ctx.req.accessToken.userId;
const workerModel = Self.app.models.Worker;
const targetTimeEntry = await Self.findById(id);
const hasRightsToDelete = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk);
const isSubordinate = await workerModel.isSubordinate(ctx, targetTimeEntry.userFk);
const isTeamBoss = await Self.app.models.Account.hasRole(currentUserId, 'teamBoss');
if (!hasRightsToDelete)
const notAllowed = isSubordinate === false || (isSubordinate && currentUserId == targetTimeEntry.userFk && !isTeamBoss);
if (notAllowed)
throw new UserError(`You don't have enough privileges`);
return Self.rawSql('CALL vn.workerTimeControl_remove(?, ?)', [

View File

@ -88,6 +88,8 @@ class Controller extends Component {
}
getWorkedHours(from, to) {
this.weekTotalHours = null;
let weekTotalHours = 0;
let params = {
id: this.$params.id,
from: from,
@ -102,6 +104,7 @@ class Controller extends Component {
for (const workDay of workDays) {
workDay.dated = new Date(workDay.dated);
map.set(workDay.dated, workDay);
weekTotalHours += workDay.workedHours;
}
for (const weekDay of this.weekDays) {
@ -114,9 +117,13 @@ class Controller extends Component {
return weekDay.dated >= from && weekDay.dated <= to;
});
if (workDay) {
weekDay.expectedHours = workDay.expectedHours;
weekDay.workedHours = workDay.workedHours;
}
}
this.weekTotalHours = weekTotalHours;
});
}
@ -128,11 +135,13 @@ class Controller extends Component {
let todayInWeek = this.weekDays.find(day => day.dated.getTime() === today.getTime());
if (todayInWeek && todayInWeek.hours) {
const remainingTime = (todayInWeek.expectedHours - todayInWeek.workedHours) * 1000;
const lastKnownTime = new Date(todayInWeek.hours[todayInWeek.hours.length - 1].timed).getTime();
const finishTimeStamp = lastKnownTime + remainingTime;
if (todayInWeek && todayInWeek.hours && todayInWeek.hours.length) {
const remainingTime = todayInWeek.workedHours ? ((todayInWeek.expectedHours - todayInWeek.workedHours) * 1000) : null;
const lastKnownEntry = todayInWeek.hours[todayInWeek.hours.length - 1];
const lastKnownTime = new Date(lastKnownEntry.timed).getTime();
const finishTimeStamp = lastKnownTime && remainingTime ? lastKnownTime + remainingTime : null;
if (finishTimeStamp) {
let finishDate = new Date(finishTimeStamp);
let hour = finishDate.getHours();
let minute = finishDate.getMinutes();
@ -143,17 +152,16 @@ class Controller extends Component {
return `${hour}:${minute} h.`;
}
}
}
set weekTotalHours(totalHours) {
if (!totalHours) return this._weekTotalHours = this.formatHours(0);
this._weekTotalHours = this.formatHours(totalHours);
}
get weekTotalHours() {
if (!this.weekDays) return 0;
let total = 0;
this.weekDays.forEach(weekday => {
if (weekday.workedHours)
total += weekday.workedHours;
});
return this.formatHours(total);
return this._weekTotalHours;
}
formatHours(timestamp) {