salix/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js

702 lines
26 KiB
JavaScript
Raw Normal View History

2022-08-10 10:13:31 +00:00
/* eslint max-len: ["error", { "code": 150 }]*/
const models = require('vn-loopback/server/server').models;
2020-10-08 14:00:19 +00:00
const LoopBackContext = require('loopback-context');
2021-02-02 09:12:37 +00:00
describe('workerTimeControl add/delete timeEntry()', () => {
2020-10-08 14:00:19 +00:00
const HHRRId = 37;
const teamBossId = 13;
const employeeId = 1;
const salesPersonId = 1106;
2021-02-02 09:07:13 +00:00
const salesBossId = 19;
2022-08-10 10:13:31 +00:00
const hankPymId = 1107;
const jessicaJonesId = 1110;
const monday = 1;
const tuesday = 2;
const thursday = 4;
const friday = 5;
const saturday = 6;
const sunday = 7;
const activeCtx = {
2020-10-08 14:00:19 +00:00
accessToken: {userId: 50},
};
2022-08-10 10:13:31 +00:00
const ctx = {req: activeCtx};
2019-11-18 10:00:09 +00:00
2020-10-08 14:00:19 +00:00
beforeAll(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
2022-08-10 10:13:31 +00:00
describe('as Role errors', () => {
it('should fail to add a time entry if the target user is not a subordinate', async() => {
activeCtx.accessToken.userId = employeeId;
const workerId = 2;
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
let error;
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
try {
ctx.args = {timed: new Date(), direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId);
} catch (e) {
error = e;
}
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
expect(error).toBeDefined();
expect(error.statusCode).toBe(400);
expect(error.message).toBe(`You don't have enough privileges`);
});
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
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;
try {
ctx.args = {timed: new Date(), direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.statusCode).toBe(400);
expect(error.message).toBe(`You don't have enough privileges`);
});
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
it('should add if the current user is team boss and the target user is himself', async() => {
activeCtx.accessToken.userId = teamBossId;
const workerId = teamBossId;
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
const todayAtOne = new Date();
todayAtOne.setHours(1, 0, 0, 0);
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
ctx.args = {timed: todayAtOne, direction: 'in'};
const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
expect(createdTimeEntry.id).toBeDefined();
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
it('should try but fail to delete his own time entry', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = salesBossId;
2022-08-10 10:13:31 +00:00
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
2022-08-10 10:13:31 +00:00
const todayAtOne = new Date();
todayAtOne.setHours(1, 0, 0, 0);
2022-08-10 10:13:31 +00:00
ctx.args = {timed: todayAtOne, direction: 'in'};
const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
2022-08-10 10:13:31 +00:00
activeCtx.accessToken.userId = salesPersonId;
await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
await tx.rollback();
} catch (e) {
error = e;
await tx.rollback();
}
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
expect(error).toBeDefined();
expect(error.statusCode).toBe(400);
expect(error.message).toBe(`You don't have enough privileges`);
});
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
it('should delete the created time entry for the team boss as himself', async() => {
activeCtx.accessToken.userId = teamBossId;
const workerId = teamBossId;
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
const todayAtOne = new Date();
todayAtOne.setHours(1, 0, 0, 0);
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
ctx.args = {timed: todayAtOne, direction: 'in'};
const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
expect(createdTimeEntry.id).toBeDefined();
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
expect(deletedTimeEntry).toBeNull();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
it('should delete the created time entry for the team boss as HHRR', async() => {
activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
const todayAtOne = new Date();
todayAtOne.setHours(1, 0, 0, 0);
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
ctx.args = {timed: todayAtOne, direction: 'in'};
const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
2021-02-02 09:07:13 +00:00
2022-08-10 10:13:31 +00:00
expect(createdTimeEntry.id).toBeDefined();
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
expect(deletedTimeEntry).toBeNull();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
it('should edit the created time entry for the team boss as HHRR', async() => {
activeCtx.accessToken.userId = HHRRId;
const workerId = teamBossId;
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
2021-06-17 13:09:04 +00:00
2022-08-10 10:13:31 +00:00
const todayAtOne = new Date();
todayAtOne.setHours(1, 0, 0, 0);
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
ctx.args = {timed: todayAtOne, 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;
}
});
2021-06-17 13:09:04 +00:00
});
2022-08-10 10:13:31 +00:00
describe('WorkerTimeControl_clockIn calls', () => {
it('should fail to add a time entry if the target user has an absence that day', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
const date = new Date();
date.setDate(date.getDate() - 16);
date.setHours(8, 0, 0);
let error;
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
const tx = await models.WorkerTimeControl.beginTransaction({});
2021-06-17 13:09:04 +00:00
const options = {transaction: tx};
2022-08-10 10:13:31 +00:00
try {
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
2020-10-08 14:00:19 +00:00
2022-08-10 10:13:31 +00:00
expect(error.message).toBe(`No está permitido trabajar`);
});
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
it('should fail to add a time entry for a worker without an existing contract', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
const date = new Date();
date.setFullYear(date.getFullYear() - 2);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
try {
const options = {transaction: tx};
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`No hay un contrato en vigor`);
});
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
describe('direction errors', () => {
it('should throw an error when trying "in" direction twice', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date.setHours(10, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Dirección incorrecta`);
});
it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(9, 0, 0);
ctx.args = {timed: date, direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date.setHours(10, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Dirección incorrecta`);
});
it('Should throw an error when trying "out" before closing a "middle" couple', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(9, 0, 0);
ctx.args = {timed: date, direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date.setHours(10, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Dirección incorrecta`);
});
it('should throw an error when trying "middle" after "out"', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(9, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date.setHours(10, 0, 0);
ctx.args = {timed: date, direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Dirección incorrecta`);
});
it('should throw an error when trying "out" direction twice', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(9, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date.setHours(10, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Dirección incorrecta`);
});
});
2019-11-18 10:00:09 +00:00
2022-08-10 10:13:31 +00:00
describe('12h rest', () => {
it('should throw an error when the 12h rest is not fulfilled yet', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(16, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date = weekDay(date, tuesday);
date.setHours(4, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Descanso diario 12h.`);
});
it('should not fail as the 12h rest is fulfilled', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(16, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date = weekDay(date, tuesday);
date.setHours(4, 1, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).not.toBeDefined;
});
});
describe('for 3500kg drivers with enforced 9h rest', () => {
it('should throw an error when the 9h enforced rest is not fulfilled', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = jessicaJonesId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(16, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date = weekDay(date, tuesday);
date.setHours(1, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Descanso diario 9h.`);
});
it('should not fail when the 9h enforced rest is fulfilled', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = jessicaJonesId;
let date = new Date();
date.setDate(date.getDate() - 21);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(16, 0, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date = weekDay(date, tuesday);
date.setHours(1, 1, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).not.toBeDefined;
});
});
describe('for 36h weekly rest', () => {
it('should throw an error when the 36h weekly rest is not fulfilled', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setMonth(date.getMonth() - 2);
date.setDate(1);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
await populateWeek(date, monday, sunday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, thursday, ctx, workerId, options);
date = weekDay(date, friday);
date.setHours(7, 59, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
try {
date.setHours(8, 1, 0);
ctx.args = {timed: date, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Descanso semanal 36h. / 72h.`);
});
it('should throw an error when the 36h weekly rest is not fulfilled again', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setMonth(date.getMonth() - 2);
date.setDate(1);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
await populateWeek(date, monday, sunday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, thursday, ctx, workerId, options);
try {
date = weekDay(date, saturday);
date.setHours(3, 59, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Descanso semanal 36h. / 72h.`);
});
});
describe('for 72h weekly rest', () => {
it('should throw when the 72h weekly rest is not fulfilled yet', async() => {
activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId;
let date = new Date();
date.setMonth(date.getMonth() - 2);
date.setDate(1);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
await populateWeek(date, monday, sunday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, thursday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, friday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, saturday, ctx, workerId, options);
date = nextWeek(date);
await populateWeek(date, monday, saturday, ctx, workerId, options);
date = lastWeek(date);
try {
date = weekDay(date, sunday);
date.setHours(8, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toBe(`Descanso semanal 36h. / 72h.`);
});
});
2019-11-18 10:00:09 +00:00
});
});
2022-08-10 10:13:31 +00:00
function weekDay(date, dayToSet) {
const currentDay = date.getDay();
const distance = dayToSet - currentDay;
date.setDate(date.getDate() + distance);
return date;
}
function nextWeek(date) {
const sunday = 7;
const currentDay = date.getDay();
let newDate = date;
if (currentDay != 0)
newDate = weekDay(date, sunday);
newDate.setDate(newDate.getDate() + 1);
return newDate;
}
function lastWeek(date) {
const monday = 1;
newDate = weekDay(date, monday);
newDate.setDate(newDate.getDate() - 1);
return newDate;
}
async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) {
const dateStart = new Date(weekDay(date, dayStart));
const dateEnd = new Date(dateStart);
dateEnd.setDate(dateStart.getDate() + dayEnd);
for (let i = dayStart; i <= dayEnd; i++) {
dateStart.setHours(8, 0, 0);
ctx.args = {timed: dateStart, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
dateStart.setHours(16, 0, 0);
ctx.args = {timed: dateStart, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
dateStart.setDate(dateStart.getDate() + 1);
}
}