DELIMITER $$ CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`workerTimeControl_clockIn`( vWorkerFk INT, vTimed DATETIME, vDirection VARCHAR(10), vDevice VARCHAR(255) ) BEGIN /** * Verifica si el empleado puede fichar * @param vWorkerFk Identificador del trabajador * @param vTimed Balor de la fichada, IF vTimed IS NULL vTimed = NOW * @param vDirection Solo se pueden pasa los valores del campo * workerTimeControl.direction ENUM('in', 'out', 'middle') * @param vDevice Dispositivo en el que se ha fichado * @return Si todo es correcto, retorna el número de id la tabla workerTimeControl. * Si hay algún problema, devuelve el mesaje que se debe mostrar al usuario * Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá * fichada a la tabla vn.workerTimeControl */ DECLARE vLastIn DATETIME; DECLARE vLastOut DATETIME; DECLARE vNextIn DATETIME; DECLARE vNextOut DATETIME; DECLARE vNextDirection ENUM('in', 'out'); DECLARE vLastDirection ENUM('in', 'out'); DECLARE vDayMaxTime INTEGER; DECLARE vDayBreak INT; DECLARE vShortWeekBreak INT; DECLARE vLongWeekBreak INT; DECLARE vWeekScope INT; DECLARE vMailTo VARCHAR(50) DEFAULT NULL; DECLARE vUserName VARCHAR(50) DEFAULT NULL; DECLARE vIsError BOOLEAN DEFAULT FALSE; DECLARE vErrorMessage VARCHAR(255) DEFAULT NULL; DECLARE vErrorCode VARCHAR(50); DECLARE vDated DATE; DECLARE vIsAllowedToWork VARCHAR(50); DECLARE vIsManual BOOLEAN DEFAULT TRUE; DECLARE vMaxWorkShortCycle INT; DECLARE vMaxWorkLongCycle INT; DECLARE EXIT HANDLER FOR SQLSTATE '45000' BEGIN SELECT CONCAT(u.name, '@verdnatura.es'), CONCAT(w.firstName, ' ', w.lastName) INTO vMailTo, vUserName FROM account.user u JOIN worker w ON w.bossFk = u.id WHERE w.id = vWorkerFk; SELECT `description` INTO vErrorMessage FROM workerTimeControlError WHERE `code` = vErrorCode; IF vErrorMessage IS NULL THEN SET vErrorMessage = 'Error sin definir'; END IF; SELECT vErrorMessage `error`; SELECT CONCAT(vUserName, ' no ha podido fichar por el siguiente problema: ', vErrorMessage) INTO vErrorMessage; CALL mail_insert( vMailTo, vMailTo, 'Error al fichar', vErrorMessage); END; IF (vTimed IS NULL) THEN SET vTimed = util.VN_NOW(); SET vIsManual = FALSE; END IF; SET vDated = DATE(vTimed); SELECT IF(pc.code = 'driverCE', wc.dayBreakDriver, wc.dayBreak), wc.shortWeekBreak, wc.longWeekBreak, wc.weekScope, wc.dayMaxTime, wc.maxWorkShortCycle, wc.maxWorkLongCycle INTO vDayBreak, vShortWeekBreak, vLongWeekBreak, vWeekScope, vDayMaxTime, vMaxWorkShortCycle, vMaxWorkLongCycle FROM business b JOIN professionalCategory pc ON pc.id = b.workerBusinessProfessionalCategoryFk JOIN workerTimeControlConfig wc WHERE b.workerFk = vWorkerFk AND vDated BETWEEN b.started AND IFNULL(b.ended, vDated); -- CONTRATO EN VIGOR IF vDayBreak IS NULL THEN SET vErrorCode = 'INACTIVE_BUSINESS'; CALL util.throw(vErrorCode); END IF; -- FICHADAS A FUTURO IF vTimed > util.VN_NOW() + INTERVAL 1 MINUTE THEN SET vErrorCode = 'IS_NOT_ALLOWED_FUTURE'; CALL util.throw(vErrorCode); END IF; -- VERIFICAR SI ESTÁ PERMITIDO TRABAJAR CALL timeBusiness_calculateByWorker(vWorkerFk, vDated, vDated); SELECT isAllowedToWork INTO vIsAllowedToWork FROM tmp.timeBusinessCalculate; DROP TEMPORARY TABLE tmp.timeBusinessCalculate; IF NOT vIsAllowedToWork THEN SET vErrorCode = 'IS_NOT_ALLOWED_WORK'; CALL util.throw(vErrorCode); END IF; -- DIRECCION CORRECTA CALL workerTimeControl_direction(vWorkerFk, vTimed); IF (SELECT IF((IF(option1 IN ('inMiddle', 'outMiddle'), 'middle', option1) <> vDirection AND IF(option2 IN ('inMiddle', 'outMiddle'), 'middle', IFNULL(option2, '')) <> vDirection) OR (option1 IS NULL AND option2 IS NULL), TRUE , FALSE) FROM tmp.workerTimeControlDirection ) THEN SET vIsError = TRUE; END IF; IF vIsError THEN SET vErrorCode = 'WRONG_DIRECTION'; IF(SELECT option1 IS NULL AND option2 IS NULL FROM tmp.workerTimeControlDirection) THEN SET vErrorCode = 'DAY_MAX_TIME'; END IF; CALL util.throw(vErrorCode); END IF; DROP TEMPORARY TABLE tmp.workerTimeControlDirection; -- FICHADAS IMPARES SELECT timed INTO vLastIn FROM workerTimeControl WHERE userFk = vWorkerFk AND direction = 'in' AND timed < vTimed ORDER BY timed DESC LIMIT 1; IF (SELECT IF(vDirection = 'in', MOD(COUNT(*), 2) , IF (vDirection = 'out', NOT MOD(COUNT(*), 2), FALSE)) FROM workerTimeControl WHERE userFk = vWorkerFk AND timed BETWEEN vLastIn AND vTimed ) THEN SET vErrorCode = 'ODD_WORKERTIMECONTROL'; CALL util.throw(vErrorCode); END IF; -- DESCANSO DIARIO SELECT timed INTO vLastOut FROM workerTimeControl WHERE userFk = vWorkerFk AND direction = 'out' AND timed < vTimed ORDER BY timed DESC LIMIT 1; SELECT timed INTO vNextIn FROM workerTimeControl WHERE userFk = vWorkerFk AND direction = 'in' AND timed > vTimed ORDER BY timed ASC LIMIT 1; CASE vDirection WHEN 'in' THEN IF UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(vLastOut) <= vDayBreak THEN SET vIsError = TRUE; END IF; WHEN 'out' THEN IF UNIX_TIMESTAMP(vNextIn) - UNIX_TIMESTAMP(vTimed) <= vDayBreak THEN SET vIsError = TRUE; END IF; ELSE BEGIN END; END CASE; IF vIsError THEN SET vErrorCode = 'BREAK_DAY'; CALL util.throw(vErrorCode); END IF; IF (vDirection IN('in', 'out')) THEN -- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA SELECT timed INTO vNextOut FROM workerTimeControl WHERE userFk = vWorkerFk AND direction = 'out' AND timed > vTimed ORDER BY timed ASC LIMIT 1; SELECT direction INTO vNextDirection FROM workerTimeControl WHERE userFk = vWorkerFk AND direction IN('in','out') AND timed > vTimed ORDER BY timed ASC LIMIT 1; SELECT direction INTO vLastDirection FROM workerTimeControl WHERE userFk = vWorkerFk AND direction IN('in', 'out') AND timed < vTimed ORDER BY timed ASC LIMIT 1; IF (vDirection ='in' AND vNextDirection = 'out' AND UNIX_TIMESTAMP(vNextOut) - UNIX_TIMESTAMP(vTimed) > vDayMaxTime) OR (vDirection ='out' AND vLastDirection = 'in' AND UNIX_TIMESTAMP(vTimed) -UNIX_TIMESTAMP(vLastIn) > vDayMaxTime) THEN SET vErrorCode = 'DAY_MAX_TIME'; CALL util.throw(vErrorCode); END IF; -- VERIFICA DESCANSO SEMANAL WITH wtc AS( (SELECT timed FROM vn.workerTimeControl WHERE userFk = vWorkerFk AND direction IN ('in', 'out') AND timed BETWEEN vTimed - INTERVAL (vWeekScope * 2) SECOND AND vTimed + INTERVAL (vWeekScope * 2) SECOND ) UNION (SELECT vTimed) ), wtcGap AS( SELECT timed, TIMESTAMPDIFF(SECOND, LAG(timed) OVER (ORDER BY timed), timed) gap FROM wtc ORDER BY timed ), wtcBreak AS( SELECT timed, IF(IFNULL(gap, 0) > vShortWeekBreak, TRUE, FALSE) hasShortBreak, IF(IFNULL(gap, 0) > vLongWeekBreak, TRUE, FALSE) hasLongBreak FROM wtcGap ORDER BY timed ), wtcBreakCounter AS( SELECT timed, SUM(hasShortBreak) OVER (ORDER BY timed) breakCounter , LEAD(hasLongBreak) OVER (ORDER BY timed) nextHasLongBreak FROM wtcBreak )SELECT TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed)) > vMaxWorkLongCycle OR (TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed))> vMaxWorkShortCycle AND NOT SUM(IFNULL(nextHasLongBreak, 1))) hasError INTO vIsError FROM wtcBreakCounter GROUP BY breakCounter HAVING hasError LIMIT 1; IF vIsError THEN SET vErrorCode = 'BREAK_WEEK'; CALL util.throw(vErrorCode); END IF; END IF; -- Se permite fichar INSERT INTO workerTimeControl(userFk, timed, direction, device, `manual`) VALUES(vWorkerFk, vTimed, vDirection, vDevice, vIsManual); SELECT LAST_INSERT_ID() id; END$$ DELIMITER ;