From 4c064f8a8003e77dfb56cab9a713f2e5dad0b4bd Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 30 Nov 2021 09:11:23 +0100 Subject: [PATCH 1/3] feat(worker_calendar): add restrictions to add absence and to add hours recorded --- loopback/locale/es.json | 4 ++- .../worker-time-control/addTimeEntry.js | 15 +++++++++- .../specs/timeEntry.spec.js | 17 +++++++++++ .../back/methods/worker/createAbsence.js | 9 ++++++ .../worker/specs/createAbsence.spec.js | 28 +++++++++++++++++++ modules/worker/back/models/worker-labour.json | 3 ++ 6 files changed, 74 insertions(+), 2 deletions(-) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index a54d2169f..2611ee0dd 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -214,5 +214,7 @@ "You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente", "The PDF document does not exists": "El documento PDF no existe. Prueba a regenerarlo desde la opción 'Regenerar PDF factura'", "The type of business must be filled in basic data": "El tipo de negocio debe estar rellenado en datos básicos", - "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días" + "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días", + "The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día", + "The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día" } \ No newline at end of file diff --git a/modules/worker/back/methods/worker-time-control/addTimeEntry.js b/modules/worker/back/methods/worker-time-control/addTimeEntry.js index 80786b723..62269afa3 100644 --- a/modules/worker/back/methods/worker-time-control/addTimeEntry.js +++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js @@ -47,11 +47,24 @@ module.exports = Self => { throw new UserError(`You don't have enough privileges`); const timed = new Date(args.timed); + timed.setHours(0, 0, 0, 0); + + query = `SELECT * FROM vn.workerLabour WHERE workerFk = ? AND (ended >= ? OR ended IS NULL);`; + const [workerLabour] = await Self.rawSql(query, [workerId, timed]); + const hasAbsence = await models.Calendar.findOne({ + where: { + businessFk: workerLabour.businessFk, + dated: timed + } + }); + + if (hasAbsence) + throw new UserError(`The worker has a marked absence that day`); return models.WorkerTimeControl.create({ userFk: workerId, direction: args.direction, - timed: timed, + timed: args.timed, manual: true }, myOptions); }; diff --git a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js index 5829653cc..0c2914934 100644 --- a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js +++ b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js @@ -77,6 +77,23 @@ describe('workerTimeControl add/delete timeEntry()', () => { } }); + it('should fail to add a time entry if the target user has absent that day', async() => { + activeCtx.accessToken.userId = salesBossId; + const workerId = salesPersonId; + let error; + + let calendar = await app.models.Calendar.findById(3); + + try { + ctx.args = {timed: new Date(calendar.dated), direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId); + } catch (e) { + error = e; + } + + expect(error.message).toBe(`The worker has a marked absence that day`); + }); + it('should try but fail to delete his own time entry', async() => { activeCtx.accessToken.userId = salesBossId; const workerId = salesBossId; diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index 89830197d..a3b26a730 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -65,6 +65,15 @@ module.exports = Self => { if (args.dated < labour.started || (labour.ended != null && args.dated > labour.ended)) throw new UserError(`The contract was not active during the selected date`); + query = `SELECT * + FROM vn.workerTimeControl + WHERE userFk = ? AND timed BETWEEN DATE(?) AND CONCAT(DATE(?), ' 23:59:59') + LIMIT 1;`; + const [hasHoursRecorded] = await Self.rawSql(query, [args.id, args.dated, args.dated]); + + if (hasHoursRecorded) + throw new UserError(`The worker has hours recorded that day`); + const result = await Self.rawSql( `SELECT COUNT(*) halfHolidayCounter FROM vn.calendar c diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index 0d6ebfc80..e9f6b9d9c 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -102,4 +102,32 @@ describe('Worker createAbsence()', () => { expect(error.message).toEqual(`Cannot add more than one '1/2 day vacation'`); }); + + it(`should throw an error when adding a absence if the worker has hours recorded that day`, async() => { + const ctx = { + req: {accessToken: {userId: 19}}, + args: { + id: 1106, + businessFk: 1106, + absenceTypeId: 6, + dated: new Date() + } + }; + + const tx = await app.models.Calendar.beginTransaction({}); + + let error; + try { + const options = {transaction: tx}; + + await app.models.Worker.createAbsence(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toEqual(`The worker has hours recorded that day`); + }); }); diff --git a/modules/worker/back/models/worker-labour.json b/modules/worker/back/models/worker-labour.json index 8ad7bf41e..b80090e57 100644 --- a/modules/worker/back/models/worker-labour.json +++ b/modules/worker/back/models/worker-labour.json @@ -11,6 +11,9 @@ "id": true, "type": "Number" }, + "workerFk": { + "type": "Number" + }, "started": { "type": "date" }, From 21bd625b3bfde0c285d933ca252e7547c6af67cc Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 30 Nov 2021 12:31:02 +0100 Subject: [PATCH 2/3] add exceprion to half day absence --- .../worker-time-control/addTimeEntry.js | 19 +++++++++++-------- .../back/methods/worker/createAbsence.js | 8 +++++--- .../worker/specs/createAbsence.spec.js | 4 ++-- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/worker/back/methods/worker-time-control/addTimeEntry.js b/modules/worker/back/methods/worker-time-control/addTimeEntry.js index 62269afa3..324b8cf26 100644 --- a/modules/worker/back/methods/worker-time-control/addTimeEntry.js +++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js @@ -46,21 +46,24 @@ module.exports = Self => { if (isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss)) throw new UserError(`You don't have enough privileges`); - const timed = new Date(args.timed); - timed.setHours(0, 0, 0, 0); + const minTime = new Date(args.timed); + minTime.setHours(0, 0, 0, 0); query = `SELECT * FROM vn.workerLabour WHERE workerFk = ? AND (ended >= ? OR ended IS NULL);`; - const [workerLabour] = await Self.rawSql(query, [workerId, timed]); - const hasAbsence = await models.Calendar.findOne({ + const [workerLabour] = await Self.rawSql(query, [workerId, minTime]); + const absence = await models.Calendar.findOne({ where: { businessFk: workerLabour.businessFk, - dated: timed + dated: minTime } }); + if (absence) { + const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk, null, myOptions); + const isNotHalfAbsence = absenceType.id != 6 && absenceType.id != 15 && absenceType.id != 21; - if (hasAbsence) - throw new UserError(`The worker has a marked absence that day`); - + if (isNotHalfAbsence) + throw new UserError(`The worker has a marked absence that day`); + } return models.WorkerTimeControl.create({ userFk: workerId, direction: args.direction, diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index a3b26a730..bec7d64f4 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -71,10 +71,12 @@ module.exports = Self => { LIMIT 1;`; const [hasHoursRecorded] = await Self.rawSql(query, [args.id, args.dated, args.dated]); - if (hasHoursRecorded) + const isNotHalfAbsence = args.absenceTypeId != 6 && args.absenceTypeId != 15 && args.absenceTypeId != 21; + + if (hasHoursRecorded && isNotHalfAbsence) throw new UserError(`The worker has hours recorded that day`); - const result = await Self.rawSql( + const [result] = await Self.rawSql( `SELECT COUNT(*) halfHolidayCounter FROM vn.calendar c JOIN postgresql.business b ON b.business_id = c.businessFk @@ -85,7 +87,7 @@ module.exports = Self => { AND c.dated BETWEEN util.firstDayOfYear(CURDATE()) AND LAST_DAY(DATE_ADD(NOW(), INTERVAL 12-MONTH(NOW()) MONTH))`, [args.id]); - const hasHalfHoliday = result[0].halfHolidayCounter > 0; + const hasHalfHoliday = result.halfHolidayCounter > 0; const isHalfHoliday = args.absenceTypeId == 6; if (isHalfHoliday && hasHalfHoliday) diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index e9f6b9d9c..c9bc56ae6 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -103,13 +103,13 @@ describe('Worker createAbsence()', () => { expect(error.message).toEqual(`Cannot add more than one '1/2 day vacation'`); }); - it(`should throw an error when adding a absence if the worker has hours recorded that day`, async() => { + it(`should throw an error when adding a absence if the worker has hours recorded that day and not is a half absence`, async() => { const ctx = { req: {accessToken: {userId: 19}}, args: { id: 1106, businessFk: 1106, - absenceTypeId: 6, + absenceTypeId: 1, dated: new Date() } }; From 238577ec8fb383228190ab296070767b75817646 Mon Sep 17 00:00:00 2001 From: vicent Date: Tue, 30 Nov 2021 13:57:17 +0100 Subject: [PATCH 3/3] refactor: change ids by codes and add fixtures --- db/changes/10390-constitution/00-absenceType.sql | 3 +++ db/dump/fixtures.sql | 1 + .../back/methods/worker-time-control/addTimeEntry.js | 5 +++-- modules/worker/back/methods/worker/createAbsence.js | 12 ++++++++---- .../back/methods/worker/specs/createAbsence.spec.js | 4 +++- 5 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 db/changes/10390-constitution/00-absenceType.sql diff --git a/db/changes/10390-constitution/00-absenceType.sql b/db/changes/10390-constitution/00-absenceType.sql new file mode 100644 index 000000000..7de22807c --- /dev/null +++ b/db/changes/10390-constitution/00-absenceType.sql @@ -0,0 +1,3 @@ +UPDATE vn.absenceType + SET code='halfPaidLeave' + WHERE id=15 AND name='Permiso retribuido 1/2 día' AND rgb='#5151c0' AND code IS NULL AND permissionRate=NULL AND holidayEntitlementRate=0.50 AND discountRate=0.00; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index b480a6d78..16e6ba5b2 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1878,6 +1878,7 @@ INSERT INTO `postgresql`.`calendar_state` (`calendar_state_id`, `type`, `rgb`, ` (1, 'Holidays', '#FF4444', 'holiday', 0), (2, 'Leave of absence', '#C71585', 'absence', 0), (6, 'Half holiday', '#E65F00', 'halfHoliday', 0), + (15, 'Half Paid Leave', '#5151c0', 'halfPaidLeave', 0), (20, 'Furlough', '#97B92F', 'furlough', 1), (21, 'Furlough half day', '#778899', 'halfFurlough', 0.5); diff --git a/modules/worker/back/methods/worker-time-control/addTimeEntry.js b/modules/worker/back/methods/worker-time-control/addTimeEntry.js index 324b8cf26..06356cdaf 100644 --- a/modules/worker/back/methods/worker-time-control/addTimeEntry.js +++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js @@ -59,8 +59,9 @@ module.exports = Self => { }); if (absence) { const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk, null, myOptions); - const isNotHalfAbsence = absenceType.id != 6 && absenceType.id != 15 && absenceType.id != 21; - + const isNotHalfAbsence = absenceType.code != 'halfHoliday' + && absenceType.code != 'halfPaidLeave' + && absenceType.code != 'halfFurlough'; if (isNotHalfAbsence) throw new UserError(`The worker has a marked absence that day`); } diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index bec7d64f4..44bda5627 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -69,9 +69,13 @@ module.exports = Self => { FROM vn.workerTimeControl WHERE userFk = ? AND timed BETWEEN DATE(?) AND CONCAT(DATE(?), ' 23:59:59') LIMIT 1;`; - const [hasHoursRecorded] = await Self.rawSql(query, [args.id, args.dated, args.dated]); + const [hasHoursRecorded] = await Self.rawSql(query, [id, args.dated, args.dated]); - const isNotHalfAbsence = args.absenceTypeId != 6 && args.absenceTypeId != 15 && args.absenceTypeId != 21; + const absenceType = await models.AbsenceType.findById(args.absenceTypeId, null, myOptions); + + const isNotHalfAbsence = absenceType.code != 'halfHoliday' + && absenceType.code != 'halfPaidLeave' + && absenceType.code != 'halfFurlough'; if (hasHoursRecorded && isNotHalfAbsence) throw new UserError(`The worker has hours recorded that day`); @@ -85,10 +89,10 @@ module.exports = Self => { WHERE c.dayOffTypeFk = 6 AND pe.workerFk = ? AND c.dated BETWEEN util.firstDayOfYear(CURDATE()) - AND LAST_DAY(DATE_ADD(NOW(), INTERVAL 12-MONTH(NOW()) MONTH))`, [args.id]); + AND LAST_DAY(DATE_ADD(NOW(), INTERVAL 12-MONTH(NOW()) MONTH))`, [id]); const hasHalfHoliday = result.halfHolidayCounter > 0; - const isHalfHoliday = args.absenceTypeId == 6; + const isHalfHoliday = absenceType.code === 'halfHoliday'; if (isHalfHoliday && hasHalfHoliday) throw new UserError(`Cannot add more than one '1/2 day vacation'`); diff --git a/modules/worker/back/methods/worker/specs/createAbsence.spec.js b/modules/worker/back/methods/worker/specs/createAbsence.spec.js index c9bc56ae6..7214e815e 100644 --- a/modules/worker/back/methods/worker/specs/createAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/createAbsence.spec.js @@ -77,7 +77,7 @@ describe('Worker createAbsence()', () => { it(`should throw an error when adding a "Half holiday" absence if there's already one`, async() => { const ctx = { - req: {accessToken: {userId: 19}}, + req: {accessToken: {userId: 9}}, args: { id: 1, businessFk: 1, @@ -85,6 +85,7 @@ describe('Worker createAbsence()', () => { dated: new Date() } }; + const workerId = 1; const tx = await app.models.Calendar.beginTransaction({}); @@ -113,6 +114,7 @@ describe('Worker createAbsence()', () => { dated: new Date() } }; + const workerId = 1106; const tx = await app.models.Calendar.beginTransaction({});