DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`workerJourney_replace`(
	vDatedFrom DATE, 
	vDatedTo DATE, 
	vWorkerFk INT)
BEGIN
/**
 * Actualiza la tabla workerJourney para que actue como caché permanente revisable 
 * de las jornadas laborales.
 * @param vDatedFrom workerTimeControl
 * @param vDatedTo workerTimeControl
 * @param vWorkerFk, en caso de pasar 0 ó NULL se aplica a todos
 */	
	DECLARE vDatedTimeTo DATETIME;
	DECLARE vMaxTimePerDay INT; 
	DECLARE vMinHoursToBreak DECIMAL(10,2);
	DECLARE vBreakHours DECIMAL(10,2);
	DECLARE vMonthByYear INT;
	DECLARE vWeeksByYear INT DEFAULT 52;
	DECLARE vMonthsByYear INT DEFAULT 12;
	DECLARE vDaysByWeek INT DEFAULT 5;
	DECLARE vDatedFromYesterday DATETIME;
	DECLARE vDatedToTomorrow DATETIME; 
	
	SELECT DATE_SUB(vDatedFrom, INTERVAL 1 DAY), DATE_ADD(vDatedTo, INTERVAL 1 DAY) 
		INTO vDatedFromYesterday, vDatedToTomorrow;

	SET vDatedTimeTo = util.dayEnd(vDatedTo);
	
	SELECT minHoursToBreak, breakHours, maxTimePerDay 
			INTO vMinHoursToBreak, vBreakHours, vMaxTimePerDay
		FROM workerTimeControlConfig;

	DELETE FROM workerJourney
		WHERE dated BETWEEN vDatedFrom AND vDatedTo 
			AND NOT isPaid
			AND NOT isUpdated
			AND (vWorkerFk IS NULL OR userFK = vWorkerFk); 

	-- Se inserta todos los días del periodo por worker y business en el rango de fechas. 
	INSERT IGNORE INTO workerJourney(userFk, dated, businessFk)
		 SELECT b.workerFk, t.dated, b.id
				FROM business b 
					JOIN time t ON t.dated BETWEEN b.started AND IFNULL(b.ended,util.VN_CURDATE())
				WHERE t.dated BETWEEN vDatedFrom AND vDatedTo
					AND (vWorkerFk IS NULL OR b.workerFk = vWorkerFk)
				ORDER BY b.workerFk, t.dated, b.id;
	
	DROP TEMPORARY TABLE IF EXISTS tmp.`user`;

	IF vWorkerFk THEN
		CALL timeControl_calculateByUser(vWorkerFk, vDatedFrom , vDatedTimeTo);
		CREATE TEMPORARY TABLE IF NOT EXISTS tmp.`user` 
			SELECT vWorkerFk userFk;	
	ELSE
		CALL timeControl_calculateAll(vDatedFrom, vDatedTimeTo);
		CREATE TEMPORARY TABLE IF NOT EXISTS tmp.`user` 
			SELECT id userFk
				FROM worker w;
	END IF; 

	UPDATE workerJourney wj
		JOIN tmp.timeControlCalculate t ON wj.dated = t.dated AND wj.userFk = t.userFk
			SET wj.total = CAST(IF(t.timeWorkDecimal >= vMinHoursToBreak, 
					t.timeWorkDecimal - vBreakHours, 
					t.timeWorkDecimal) AS DECIMAL (10,2)),
				wj.lunch = IF(t.timeWorkDecimal >= vMinHoursToBreak , vBreakHours , 0)
		WHERE wj.dated BETWEEN vDatedFrom AND vDatedTo
			AND NOT wj.isPaid
			AND NOT wj.isUpdated
			AND (vWorkerFk IS NULL OR wj.userFk = vWorkerFk);
	
	-- NOCTURNIDAD 
	CALL timeControl_getError(vDatedFrom, vDatedTimeTo);

	SET @vIsOdd := TRUE;
	SET @vDated := NULL;

	DROP TEMPORARY TABLE IF EXISTS tmp.workerTimeControl;
	CREATE TEMPORARY TABLE tmp.workerTimeControl
			(INDEX (userFk), INDEX (timed))
			ENGINE = MEMORY
		SELECT DISTINCT(wtc.id), 
				wtc.userFk, 
				wtc.timed, 
				wtc.direction
			FROM workerTimeControl wtc
				JOIN tmp.`user` w ON w.userFk = wtc.userFk
				LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
			WHERE wtc.timed BETWEEN vDatedFrom AND vDatedToTomorrow
				AND tce.id IS NULL
				AND (vWorkerFk IS NULL OR wtc.userFk = vWorkerFk)
			ORDER BY wtc.userFk, wtc.timed ASC;

	SELECT MAX(id) INTO @vCont 
		FROM tmp.workerTimeControl;

	DROP TEMPORARY TABLE IF EXISTS tmp.workerTimeControlAux; 
	CREATE TEMPORARY TABLE tmp.workerTimeControlAux (
		`id` int(11) ,
		`userFk` int(10) unsigned ,
		`timed` datetime,
		`direction` enum('in', 'out','middle')
		) ENGINE=MEMORY;

	SET @vIsOdd := TRUE;
	SET @lastUserFk := NULL;
	SET @lastDirection := NULL;

	-- Cambio de dia en medio de un descanso
	INSERT INTO tmp.workerTimeControlAux (id, userFk, timed, direction)
		SELECT @vCont:= @vCont + 1,
				lastUserFk,
				util.dayEnd(dated),
				'middle'
			FROM (SELECT IF(@lastUserFk <> userFk, @vIsOdd := TRUE, NULL),
						DATE(@lastTimed) dated,
						@lastUserFk lastUserFk,
						IF(direction ='middle', @vIsOdd := NOT @vIsOdd, FALSE) isOdd,
						IF(@vIsOdd 
							AND @lastDirection = 'middle' 
							AND (DATE(@lastTimed) <> date(timed) 
								OR @lastUserFk <> userFk), TRUE, FALSE) hasBreak,
						@lastTimed := wtc.timed,
						@lastUserFk := userFk,
						@lastDirection := direction
					FROM tmp.workerTimeControl wtc
					ORDER BY wtc.userFk, wtc.timed)sub
			WHERE hasBreak;

	SET @vIsOdd := TRUE;
	SET @lastUserFk := NULL;
	SET @lastDirection := NULL;

	INSERT INTO tmp.workerTimeControlAux (id, userFk, timed, direction)
		SELECT @vCont:= @vCont + 1, 
				lastUserFk, 
				DATE_ADD(sub.dated, INTERVAL 1 DAY), 
				'middle'
			FROM (SELECT IF(@lastUserFk <> userFk, @vIsOdd := TRUE, NULL),
						DATE(@lastTimed) dated,
						@lastUserFk lastUserFk,
						IF(direction ='middle', @vIsOdd := NOT @vIsOdd, FALSE) isOdd,
						IF(@vIsOdd 
							AND @lastDirection = 'middle' 
							AND (DATE(@lastTimed)<>date(timed)
								OR @lastUserFk<>userFk), TRUE, FALSE) hasBreak,
						@lastTimed := wtc.timed,
						@lastUserFk := userFk,
						@lastDirection := direction
					FROM tmp.workerTimeControl wtc
					ORDER BY wtc.userFk, wtc.timed)sub
			WHERE hasBreak;

	-- Cambio de dia
	INSERT INTO tmp.workerTimeControlAux (id, userFk, timed, direction)
		SELECT @vCont:= @vCont + 1,
				wtc.userFk, 
				util.dayEnd(date(wtc.timed)),
				'out'
			FROM workerTimeControl wtc
				JOIN tmp.`user` w ON w.userFk = wtc.userFk
				LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
			WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
				AND wtc.direction <> 'out'
			GROUP BY wtc.userFk, DATE(wtc.timed);

	INSERT INTO tmp.workerTimeControlAux (id, userFk, timed, direction)
		SELECT @vCont:= @vCont + 1, 
				wtc.userFk, 
				DATE_ADD(date(wtc.timed), INTERVAL 1 DAY),
				'in'
			FROM workerTimeControl wtc
				JOIN tmp.`user` w ON w.userFk = wtc.userFk
				LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
			WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
				AND wtc.direction <> 'out'
			GROUP BY wtc.userFk, DATE(wtc.timed);

	INSERT INTO tmp.workerTimeControl (id, userFk, timed, direction)
		SELECT id, userFk, timed, direction
			FROM tmp.workerTimeControlAux;

	SET @lastUserFk := NULL;
	SET @vIsOdd := TRUE;
	SET @vDated := NULL;
	SET @vLastTimed := NULL;

	UPDATE workerJourney wj
			JOIN (SELECT sub.dated, userFk, SUM(NigthlyHours) NigthlyHours
					FROM(SELECT (@vIsOdd := NOT @vIsOdd) isOdd,
								IF(direction='in', @vIsOdd := TRUE, @vIsOdd := @vIsOdd),
								IF(@vIsOdd, @vLastTimed:=UNIX_TIMESTAMP(timed),@vLastTimed),
								IF(direction='in', @vDated := DATE(wtc.timed), @vDated ) dated,
								wtc.userFk,
								IF(UNIX_TIMESTAMP(timed)- @vLastTimed < vMaxTimePerDay, 
									workerNigthlyHours_calculate(FROM_UNIXTIME(@vLastTimed), wtc.timed), 
									0) NigthlyHours,
								@lastUserFk := userFk
							FROM (SELECT DISTINCT(wtc.id), wtc.userFk, wtc.timed, wtc.direction
									FROM tmp.workerTimeControl wtc
										LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
									WHERE wtc.timed BETWEEN vDatedFrom AND util.dayEnd(vDatedToTomorrow)
										AND tce.id IS NULL
									ORDER BY userFk, timed ASC
									LIMIT 10000000000000000000
								) wtc
							ORDER BY wtc.userFk, wtc.timed
						)sub
					WHERE sub.dated BETWEEN vDatedFrom AND vDatedTo	 
					GROUP BY userFk, sub.dated
				)night ON night.userFk = wj.userFk AND night.dated = wj.dated
		SET wj.nocturn = night.NigthlyHours
		WHERE NOT wj.isPaid
			AND NOT isUpdated
			AND night.NigthlyHours <> 0;

	-- Horas téoricas y precio de la hora ordinaria
	UPDATE workerJourney wj
			JOIN business b ON b.id = wj.businessFk
			JOIN calendarType ct ON ct.id = b.calendarTypeFk
		SET wj.priceOrdinaryHour = 
				(vMonthsByYear * b.amount) / 
				(vWeeksByYear * ct.hoursWeek),
			wj.contractJourney = ct.hoursWeek / vDaysByWeek
		WHERE wj.dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR wj.userFk = vWorkerFk)
			AND NOT wj.isPaid
			AND NOT isUpdated;

	-- Precio Extras, Vacaciones y Nocturnas 
	UPDATE workerJourney wj
			JOIN(SELECT MAX(w.dated), t.dated, w.holidayInc, w.nightInc, w.extraInc
					FROM time t
						JOIN workerHourPrice w ON w.dated <= t.dated
					WHERE t.dated BETWEEN vDatedFrom AND vDatedTo
					GROUP BY t.dated
				) sub ON sub.dated = wj.dated
		SET wj.priceHolidayHour = wj.priceOrdinaryHour * sub.holidayInc,
			wj.priceNocturnHour = wj.priceOrdinaryHour * sub.nightInc,
			wj.priceExtraHour = wj.priceOrdinaryHour * sub.extraInc
		WHERE wj.dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR wj.userFk = vWorkerFk)
			AND NOT wj.isPaid
			AND NOT wj.isUpdated;

	-- Elimina el precio de las horas extras para los contratos parciales.
	UPDATE workerJourney wj
			JOIN business b ON b.id = wj.businessFk
			JOIN calendarType ct ON ct.id = b.calendarTypeFk
		SET wj.priceExtraHour = 0
		WHERE ct.isPartial
			AND wj.dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR wj.userFk = vWorkerFk)
			AND NOT wj.isPaid 
			AND NOT wj.isUpdated;

	-- Ausencias
	UPDATE workerJourney wj
			JOIN businessCalendar bc ON bc.businessFk = wj.businessFk 
				AND bc.dated = wj.dated
			JOIN absenceType ab ON ab.id = bc.absenceTypeFk
		SET wj.permission = ab.permissionRate * wj.contractJourney,
			wj.contractJourney = IF(ab.permissionRate = 1, 0, ab.permissionRate) * wj.contractJourney
		WHERE ab.permissionRate > 0
			AND wj.dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR wj.userFk = vWorkerFk)
			AND NOT wj.isPaid
			AND NOT wj.isUpdated;

	-- Sábados / Domingos
	 UPDATE workerJourney
		SET holiday = IF(DAYNAME(dated)='sábado', 0, total),
			contractJourney = 0
		WHERE DAYNAME(dated) IN ('sábado', 'domingo') 
			AND dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR userFk = vWorkerFk)
			AND NOT isPaid
			AND NOT isUpdated;

	-- Festivos por centro de trabajo
	UPDATE workerJourney wj
			JOIN calendarHolidays ch ON ch.dated = wj.dated
			JOIN business b ON b.id = wj.businessFk 
				AND b.workcenterFk = ch.workcenterFk 
		SET wj.holiday = wj.total,
			wj.permission = wj.contractJourney,
			wj.contractJourney = 0
		WHERE wj.dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR wj.userFk = vWorkerFk)
			AND NOT wj.isPaid
			AND NOT wj.isUpdated;

	-- Horas extras
	UPDATE workerJourney 
		SET extra = lunch + total - contractJourney
		WHERE dated BETWEEN vDatedFrom AND vDatedTo
			AND (vWorkerFk IS NULL OR userFk = vWorkerFk)
			AND NOT isPaid
			AND NOT isUpdated;

	DROP TEMPORARY TABLE tmp.timeControlCalculate;
	DROP TEMPORARY TABLE tmp.`user`;
	DROP TEMPORARY TABLE tmp.timeControlError;
	DROP TEMPORARY TABLE tmp.workerTimeControlAux;
	DROP TEMPORARY TABLE tmp.workerTimeControl;
END$$
DELIMITER ;