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/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..06356cdaf 100644 --- a/modules/worker/back/methods/worker-time-control/addTimeEntry.js +++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js @@ -46,12 +46,29 @@ module.exports = Self => { if (isSubordinate === false || (isSubordinate && isHimself && !isTeamBoss)) throw new UserError(`You don't have enough privileges`); - const timed = new Date(args.timed); + 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, minTime]); + const absence = await models.Calendar.findOne({ + where: { + businessFk: workerLabour.businessFk, + dated: minTime + } + }); + if (absence) { + const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk, null, myOptions); + const isNotHalfAbsence = absenceType.code != 'halfHoliday' + && absenceType.code != 'halfPaidLeave' + && absenceType.code != 'halfFurlough'; + if (isNotHalfAbsence) + 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..44bda5627 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -65,7 +65,22 @@ 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`); - const result = await Self.rawSql( + 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, [id, args.dated, args.dated]); + + 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`); + + const [result] = await Self.rawSql( `SELECT COUNT(*) halfHolidayCounter FROM vn.calendar c JOIN postgresql.business b ON b.business_id = c.businessFk @@ -74,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[0].halfHolidayCounter > 0; - const isHalfHoliday = args.absenceTypeId == 6; + const hasHalfHoliday = result.halfHolidayCounter > 0; + 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 0d6ebfc80..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({}); @@ -102,4 +103,33 @@ 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 and not is a half absence`, async() => { + const ctx = { + req: {accessToken: {userId: 19}}, + args: { + id: 1106, + businessFk: 1106, + absenceTypeId: 1, + dated: new Date() + } + }; + const workerId = 1106; + + 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" },