Merge branch 'dev' into 7912-wasteCost
gitea/salix/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Guillermo Bonet 2025-03-03 06:09:36 +00:00
commit 3871c9c505
5 changed files with 188 additions and 77 deletions

View File

@ -18,7 +18,6 @@ BEGIN
* Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá * Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá
* fichada a la tabla vn.workerTimeControl * fichada a la tabla vn.workerTimeControl
*/ */
DECLARE vLastIn DATETIME; DECLARE vLastIn DATETIME;
DECLARE vLastOut DATETIME; DECLARE vLastOut DATETIME;
DECLARE vNextIn DATETIME; DECLARE vNextIn DATETIME;
@ -40,6 +39,7 @@ BEGIN
DECLARE vIsManual BOOLEAN DEFAULT TRUE; DECLARE vIsManual BOOLEAN DEFAULT TRUE;
DECLARE vMaxWorkShortCycle INT; DECLARE vMaxWorkShortCycle INT;
DECLARE vMaxWorkLongCycle INT; DECLARE vMaxWorkLongCycle INT;
DECLARE vReentryWaitTime TIME DEFAULT NULL;
DECLARE EXIT HANDLER FOR SQLSTATE '45000' DECLARE EXIT HANDLER FOR SQLSTATE '45000'
BEGIN BEGIN
@ -59,7 +59,7 @@ BEGIN
SET vErrorMessage = 'Error sin definir'; SET vErrorMessage = 'Error sin definir';
END IF; END IF;
SELECT vErrorMessage `error`; SELECT CONCAT(vErrorMessage, IFNULL(DATE_FORMAT(vReentryWaitTime, '%i:%s'), '')) `error`;
SELECT CONCAT(vUserName, SELECT CONCAT(vUserName,
' no ha podido fichar por el siguiente problema: ', ' no ha podido fichar por el siguiente problema: ',
vErrorMessage) vErrorMessage)
@ -124,6 +124,16 @@ BEGIN
-- DIRECCION CORRECTA -- DIRECCION CORRECTA
CALL workerTimeControl_direction(vWorkerFk, vTimed); CALL workerTimeControl_direction(vWorkerFk, vTimed);
SELECT reentryWaitTime INTO vReentryWaitTime
FROM tmp.workerTimeControlDirection
LIMIT 1;
IF vReentryWaitTime IS NOT NULL THEN
SET vErrorCode = 'WAIT_TIME';
CALL util.throw(vErrorCode);
END IF;
IF (SELECT IF (SELECT
IF((IF(option1 IN ('inMiddle', 'outMiddle'), IF((IF(option1 IN ('inMiddle', 'outMiddle'),
'middle', 'middle',
@ -204,8 +214,6 @@ BEGIN
CALL util.throw(vErrorCode); CALL util.throw(vErrorCode);
END IF; END IF;
IF (vDirection IN('in', 'out')) THEN IF (vDirection IN('in', 'out')) THEN
-- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA -- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA
@ -291,5 +299,6 @@ BEGIN
SELECT LAST_INSERT_ID() id; SELECT LAST_INSERT_ID() id;
END$$ END
$$
DELIMITER ; DELIMITER ;

View File

@ -1,16 +1,21 @@
DELIMITER $$ DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`workerTimeControl_direction`(vWorkerFk VARCHAR(10), vTimed DATETIME) CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`workerTimeControl_direction`(
vWorkerFk VARCHAR(10),
vTimed DATETIME
)
BEGIN BEGIN
/** /**
* Devuelve que direcciones de fichadas son lógicas a partir de la anterior fichada * Devuelve que direcciones de fichadas son lógicas a partir de la anterior fichada,
* @param vWorkerFk Identificador del trabajador * @param vWorkerFk Identificador del trabajador
* @return (option1, option2) * @return tmp.workerTimeControlDirection(option1, option2, reentryWaitTime)
* Los valores posibles de retorno son ('in', 'inMiddle', 'outMiddle', 'out') * Valores posibles options('in', 'inMiddle', 'outMiddle', 'out')
*/ */
DECLARE vLastIn DATETIME; DECLARE vLastIn DATETIME;
DECLARE vLastMiddleIn DATETIME;
DECLARE vIsMiddleOdd BOOLEAN; DECLARE vIsMiddleOdd BOOLEAN;
DECLARE vMailTo VARCHAR(50) DEFAULT NULL; DECLARE vMailTo VARCHAR(50) DEFAULT NULL;
DECLARE vUserName VARCHAR(50) DEFAULT NULL; DECLARE vUserName VARCHAR(50) DEFAULT NULL;
DECLARE vReentryWaitTime TIME;
IF (vTimed IS NULL) THEN IF (vTimed IS NULL) THEN
SET vTimed = util.VN_NOW(); SET vTimed = util.VN_NOW();
@ -33,7 +38,8 @@ BEGIN
DROP TEMPORARY TABLE IF EXISTS tmp.workerTimeControlDirection; DROP TEMPORARY TABLE IF EXISTS tmp.workerTimeControlDirection;
CREATE TEMPORARY TABLE tmp.workerTimeControlDirection CREATE TEMPORARY TABLE tmp.workerTimeControlDirection
SELECT IF(isCorrect, option1, NULL) option1, SELECT IF(isCorrect, option1, NULL) option1,
IF(isCorrect, option2, NULL) option2 IF(isCorrect, option2, NULL) option2,
CAST(NULL AS TIME) reentryWaitTime
FROM( SELECT IF(w.direction <> 'out' AND (UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(w.timed) > wc.dayBreak), FALSE, TRUE) isCorrect, FROM( SELECT IF(w.direction <> 'out' AND (UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(w.timed) > wc.dayBreak), FALSE, TRUE) isCorrect,
CASE WHEN w.direction ='in' THEN 'inMiddle' CASE WHEN w.direction ='in' THEN 'inMiddle'
WHEN w.direction = 'out' THEN 'in' WHEN w.direction = 'out' THEN 'in'
@ -59,6 +65,26 @@ BEGIN
VALUES('in', NULL); VALUES('in', NULL);
END IF; END IF;
IF (SELECT option1 ='outMiddle' AND option2 IS NULL FROM tmp.workerTimeControlDirection) THEN
SELECT timed INTO vLastMiddleIn
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND timed < vTimed
ORDER BY timed DESC
LIMIT 1;
SELECT TIMEDIFF(vLastMiddleIn + INTERVAL wtc.maxTimeToBreak SECOND, vTimed) INTO vReentryWaitTime
FROM tmp.workerTimeControlDirection wt
JOIN workerTimeControlConfig wtc
WHERE TIME(vLastMiddleIn) BETWEEN wtc.mandatoryBreakFrom AND wtc.mandatoryBreakTo
AND TIMESTAMPDIFF(SECOND, vLastMiddleIn, vTimed) < wtc.maxTimeToBreak;
IF vReentryWaitTime THEN
UPDATE tmp.workerTimeControlDirection
SET reentryWaitTime = vReentryWaitTime;
END IF;
END IF;
IF (SELECT option1 IS NULL AND option2 IS NULL FROM tmp.workerTimeControlDirection) THEN IF (SELECT option1 IS NULL AND option2 IS NULL FROM tmp.workerTimeControlDirection) THEN
SELECT CONCAT(u.name, '@verdnatura.es'), CONCAT(w.firstName, ' ', w.lastName) SELECT CONCAT(u.name, '@verdnatura.es'), CONCAT(w.firstName, ' ', w.lastName)
INTO vMailTo, vUserName INTO vMailTo, vUserName

View File

@ -0,0 +1,8 @@
ALTER TABLE vn.workerTimeControlConfig
ADD COLUMN `mandatoryBreakFrom` time DEFAULT '12:00:00'
COMMENT 'Tiempo desde el que se obligará a realizar un descanso para jornada partida';
ALTER TABLE vn.workerTimeControlConfig
ADD COLUMN `mandatoryBreakTo` time DEFAULT '15:00:00';
INSERT INTO vn.workerTimeControlError (`code`, `description`)
VALUES ('WAIT_TIME', 'El descanso terminará en ');

View File

@ -626,6 +626,74 @@ describe('workerTimeControl clockIn()', () => {
expect(error).not.toBeDefined; expect(error).not.toBeDefined;
}); });
it('should enforce a 1-hour break if the break starts between 12:00 and 15:00', async() => {
let date = Date.vnNew();
date.setDate(date.getDate() - 2);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
try {
date.setHours(11, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(13, 30, 0);
ctx.args = {timed: date, direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(14, 0, 0);
ctx.args = {timed: date, direction: 'middle'};
let error;
try {
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.statusCode).toBe(400);
expect(error.message).toMatch(/El descanso terminará en \d{2}:\d{2}/);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should allow resuming work after a full 1-hour break', async() => {
let date = Date.vnNew();
date.setDate(date.getDate() - 2);
date = weekDay(date, monday);
let error;
const tx = await models.WorkerTimeControl.beginTransaction({});
const options = {transaction: tx};
try {
date.setHours(11, 0, 0);
ctx.args = {timed: date, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(14, 00, 0);
ctx.args = {timed: date, direction: 'middle'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
date.setHours(15, 00, 0);
ctx.args = {timed: date, direction: 'middle'};
let error;
try {
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
} catch (e) {
error = e;
}
expect(error).toBeUndefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
}); });
}); });
}); });

View File

@ -95,9 +95,9 @@ describe('workerTimeControl add/delete timeEntry()', () => {
try { try {
await populateWeek(dated, monday, monday, ctx, hankPymId, options); await populateWeek(dated, monday, monday, ctx, hankPymId, options);
dated.setHours(14, 59, 0); dated.setHours(04, 59, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
dated.setHours(15, 14, 0); dated.setHours(05, 14, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
const start = new Date(dated - 1); const start = new Date(dated - 1);
@ -124,9 +124,9 @@ describe('workerTimeControl add/delete timeEntry()', () => {
try { try {
await populateWeek(dated, monday, monday, ctx, hankPymId, options); await populateWeek(dated, monday, monday, ctx, hankPymId, options);
dated.setHours(15, 0, 0); dated.setHours(05, 0, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
dated.setHours(15, 15, 0); dated.setHours(05, 15, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
const start = new Date(dated - 1); const start = new Date(dated - 1);
@ -153,9 +153,9 @@ describe('workerTimeControl add/delete timeEntry()', () => {
try { try {
await populateWeek(dated, monday, monday, ctx, hankPymId, options); await populateWeek(dated, monday, monday, ctx, hankPymId, options);
dated.setHours(14, 59, 0); dated.setHours(04, 59, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
dated.setHours(15, 24, 0); dated.setHours(05, 24, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
const start = new Date(dated - 1); const start = new Date(dated - 1);
@ -182,9 +182,9 @@ describe('workerTimeControl add/delete timeEntry()', () => {
try { try {
await populateWeek(dated, monday, monday, ctx, hankPymId, options); await populateWeek(dated, monday, monday, ctx, hankPymId, options);
dated.setHours(15, 0, 0); dated.setHours(05, 0, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
dated.setHours(15, 25, 0); dated.setHours(05, 25, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
const start = new Date(dated - 1); const start = new Date(dated - 1);
@ -211,9 +211,9 @@ describe('workerTimeControl add/delete timeEntry()', () => {
try { try {
await populateWeek(dated, monday, monday, ctx, hankPymId, options); await populateWeek(dated, monday, monday, ctx, hankPymId, options);
dated.setHours(14, 59, 0); dated.setHours(04, 59, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
dated.setHours(15, 59, 0); dated.setHours(05, 59, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
const start = new Date(dated - 1); const start = new Date(dated - 1);
@ -240,9 +240,9 @@ describe('workerTimeControl add/delete timeEntry()', () => {
try { try {
await populateWeek(dated, monday, monday, ctx, hankPymId, options); await populateWeek(dated, monday, monday, ctx, hankPymId, options);
dated.setHours(15, 0, 0); dated.setHours(05, 0, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
dated.setHours(16, 0, 0); dated.setHours(06, 0, 0);
await addTimeEntry(ctx, dated, 'middle', hankPymId, options); await addTimeEntry(ctx, dated, 'middle', hankPymId, options);
const start = new Date(dated - 1); const start = new Date(dated - 1);
@ -286,10 +286,10 @@ async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) {
dateEnd.setDate(dateStart.getDate() + dayEnd); dateEnd.setDate(dateStart.getDate() + dayEnd);
for (let i = dayStart; i <= dayEnd; i++) { for (let i = dayStart; i <= dayEnd; i++) {
dateStart.setHours(10, 0, 0); dateStart.setHours(00, 0, 0);
ctx.args = {timed: dateStart, direction: 'in'}; ctx.args = {timed: dateStart, direction: 'in'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
dateStart.setHours(18, 0, 0); dateStart.setHours(08, 0, 0);
ctx.args = {timed: dateStart, direction: 'out'}; ctx.args = {timed: dateStart, direction: 'out'};
await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
dateStart.setDate(dateStart.getDate() + 1); dateStart.setDate(dateStart.getDate() + 1);