DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_calculateComponent`(vSelf INT, vOption VARCHAR(25))
proc: BEGIN
/**
 * Crea tabla temporal para vn.sale_recalcComponent() para recalcular los componentes
 *
 * @param vSelf Id de la venta
 * @param vOption indica en que componente pone el descuadre, NULL en casos habituales
 */
	CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
		SELECT s.id
			FROM sale s
			WHERE s.id = vSelf;
		
	CALL sale_recalcComponent(vOption);

	DROP TEMPORARY TABLE tmp.recalculateSales;
END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_checkNoComponents`(vCreatedFrom DATETIME, vCreatedTo DATETIME)
BEGIN
/**
 * Comprueba que las ventas creadas entre un rango de fechas tienen componentes
 *
 * @param vCreatedFrom inicio del rango
 * @param vCreatedTo fin del rango
 */
	DECLARE v_done BOOL DEFAULT FALSE;
	DECLARE vSaleFk INTEGER;
	DECLARE vTicketFk INTEGER;
	DECLARE vConcept VARCHAR(50);
	DECLARE vCur CURSOR FOR
		SELECT s.id
			FROM sale s
				JOIN ticket t ON t.id = s.ticketFk
				JOIN item i ON i.id = s.itemFk
				JOIN itemType tp ON tp.id = i.typeFk
				JOIN itemCategory ic ON ic.id = tp.categoryFk
				LEFT JOIN tmp.coste c ON c.id = s.id
			WHERE s.created >= vCreatedFrom AND s.created <= vCreatedTo
				AND c.id IS NULL
				AND t.agencyModeFk IS NOT NULL
                AND t.isDeleted IS FALSE
                 AND t.warehouseFk = 60
				AND ic.merchandise != FALSE
			GROUP BY s.id;

	DECLARE CONTINUE HANDLER FOR NOT FOUND
		SET v_done = TRUE; 

	DROP TEMPORARY TABLE IF EXISTS tmp.coste;

	DROP TEMPORARY TABLE IF EXISTS tmp.coste;
	CREATE TEMPORARY TABLE tmp.coste
		(PRIMARY KEY (id)) ENGINE = MEMORY
		SELECT s.id
		FROM sale s 
			JOIN item i ON i.id = s.itemFk
			JOIN itemType tp ON tp.id = i.typeFk
			JOIN itemCategory ic ON ic.id = tp.categoryFk
			JOIN saleComponent sc ON sc.saleFk = s.id
			JOIN component c ON c.id = sc.componentFk
			JOIN componentType ct ON ct.id = c.typeFk AND ct.id = 6
		WHERE s.created >= vCreatedFrom
			AND ic.merchandise != FALSE;

	OPEN vCur;

	l: LOOP
		SET v_done = FALSE;
		FETCH vCur INTO vSaleFk;

		IF v_done THEN
			LEAVE l;
		END IF;
		
		SELECT ticketFk, concept
			INTO vTicketFk, vConcept
			FROM sale
			WHERE id = vSaleFk;
			
		CALL sale_calculateComponent(vSaleFk, 'renewPrices');
	END LOOP;

	CLOSE vCur;
	DROP TEMPORARY TABLE tmp.coste;	
END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`sale_recalcComponent`(vOption VARCHAR(25))
proc: BEGIN
/**
 * Este procedimiento recalcula los componentes de un conjunto de sales, 
 * eliminando los componentes existentes e insertandolos de nuevo
 *
 * @param vOption si no se quiere forzar llamar con NULL
 * @table tmp.recalculateSales (id)
 */ 
	DECLARE vShipped DATE; 
	DECLARE vWarehouseFk SMALLINT;
	DECLARE vAgencyModeFk INT;
	DECLARE vAddressFk INT;
	DECLARE vTicketFk INT;
	DECLARE vLanded DATE; 
	DECLARE vIsEditable BOOLEAN;
	DECLARE vZoneFk INTEGER;
	DECLARE vDone BOOL DEFAULT FALSE;

	DECLARE vCur CURSOR FOR
		SELECT DISTINCT s.ticketFk
			FROM tmp.recalculateSales rs
				JOIN vn.sale s ON s.id = rs.id;

	DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; 

	OPEN vCur;

	l: LOOP
		SET vDone = FALSE;
		FETCH vCur INTO vTicketFk; 
		
		IF vDone THEN
			LEAVE l;
		END IF;

		SELECT (hasToRecalcPrice OR ts.alertLevel IS NULL) AND t.refFk IS NULL, 
					t.zoneFk, 
					t.warehouseFk, 
					t.shipped, 
					t.addressFk, 
					t.agencyModeFk, 
					t.landed
				INTO vIsEditable, 
					vZoneFk, 
					vWarehouseFk, 
					vShipped, 
					vAddressFk, 
					vAgencyModeFk, 
					vLanded
			FROM ticket t
				LEFT JOIN ticketState ts ON t.id = ts.ticketFk
				LEFT JOIN alertLevel al ON al.id = ts.alertLevel
			WHERE t.id = vTicketFk;

		CALL zone_getLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk, TRUE);
	
		IF NOT EXISTS (SELECT TRUE FROM tmp.zoneGetLanded LIMIT 1) THEN
			CALL util.throw(CONCAT('There is no zone for these parameters ', vTicketFk));
		END IF;

		IF vLanded IS NULL OR vZoneFk IS NULL THEN
	
			UPDATE ticket t
				SET t.landed = (SELECT landed FROM tmp.zoneGetLanded LIMIT 1) 
				WHERE t.id = vTicketFk AND t.landed IS NULL;

			IF vZoneFk IS NULL THEN
				SELECT zoneFk INTO vZoneFk FROM tmp.zoneGetLanded LIMIT 1;
				UPDATE ticket t
					SET t.zoneFk = vZoneFk
					WHERE t.id = vTicketFk AND t.zoneFk IS NULL;
			END IF;
			
		END IF;
	
		DROP TEMPORARY TABLE tmp.zoneGetLanded;

		-- rellena la tabla buyUltimate con la ultima compra	
		CALL buyUltimate (vWarehouseFk, vShipped);  
	
		CREATE OR REPLACE TEMPORARY TABLE tmp.sale
				(PRIMARY KEY (saleFk)) ENGINE = MEMORY
			SELECT s.id saleFk, vWarehouseFk warehouseFk
				FROM sale s
						JOIN tmp.recalculateSales rs ON s.id = rs.id
					WHERE s.ticketFk = vTicketFk;

		CREATE OR REPLACE TEMPORARY TABLE tmp.ticketLot 
			SELECT vWarehouseFk warehouseFk, NULL available, s.itemFk, bu.buyFk, vZoneFk zoneFk
				FROM sale s
					JOIN tmp.recalculateSales rs ON s.id = rs.id
					LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
				WHERE s.ticketFk = vTicketFk
				GROUP BY s.itemFk;

		CALL catalog_componentPrepare();
		CALL catalog_componentCalculate(vZoneFk, vAddressFk, vShipped, vWarehouseFk);

		IF vOption IS NULL THEN
			SET vOption = IF(vIsEditable, 'renewPrices', 'imbalance');
		END IF;

		CALL ticketComponentUpdateSale(vOption);
		CALL catalog_componentPurge();

		DROP TEMPORARY TABLE tmp.buyUltimate;
		DROP TEMPORARY TABLE tmp.sale;

	END LOOP;
	CLOSE vCur; 

END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketCalculateClon`(IN vTicketNew INT, vTicketOld INT)
BEGIN
/*
 * Recalcula los componentes un ticket clonado,
 * las lineas a precio cero fuerza para que tengan precio, el resto lo respeta
 * @param vTicketNew nuevo ticket clonado
 * @param vTicketOld icket original, a partir del qual se clonara el nuevo
*/

	REPLACE INTO orderTicket(orderFk,ticketFk)
		SELECT orderFk, vTicketNew 
			FROM orderTicket 
				WHERE ticketFk = vTicketOld;

	-- Bionizamos lineas con Preu = 0
	CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
				(PRIMARY KEY (id)) ENGINE = MEMORY
			SELECT id
				FROM sale
				WHERE ticketFk = vTicketNew AND price = 0;

	CALL sale_recalcComponent('renewPrices');

	-- Bionizamos lineas con Preu > 0
	CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
				(PRIMARY KEY (id)) ENGINE = MEMORY
			SELECT id 
				FROM sale
				WHERE ticketFk = vTicketNew AND price > 0;

	CALL sale_recalcComponent('imbalance');

	DROP TEMPORARY TABLE IF EXISTS tmp.recalculateSales;

END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketComponentUpdate`(
	 vTicketFk INT,
	vClientFk INT,
	vAgencyModeFk INT,
	vAddressFk INT,
	vWarehouseFk TINYINT,
	vCompanyFk SMALLINT,
	vShipped DATETIME,
	vLanded DATE,
	vIsDeleted BOOLEAN,
	vHasToBeUnrouted BOOLEAN,
	vOption VARCHAR(25))
BEGIN

	DECLARE EXIT HANDLER FOR SQLEXCEPTION
	BEGIN
		ROLLBACK;
		RESIGNAL;
	END;

	START TRANSACTION;

	IF (SELECT addressFk FROM ticket WHERE id = vTicketFk) <> vAddressFk THEN
	
		UPDATE ticket t 
				JOIN address a ON a.id = vAddressFk
			SET t.nickname = a.nickname
			WHERE t.id = vTicketFk;
	END IF;

	UPDATE ticket t
		SET 
			t.clientFk = vClientFk,
			t.agencyModeFk = vAgencyModeFk,
			t.addressFk = vAddressFk,
			t.warehouseFk = vWarehouseFk,
			t.companyFk = vCompanyFk,
			t.landed = vLanded,
			t.shipped = vShipped,
			t.isDeleted = vIsDeleted
		WHERE
			t.id = vTicketFk;

	IF vHasToBeUnrouted THEN
		UPDATE ticket t SET t.routeFk = NULL
			WHERE t.id = vTicketFk;
	END IF;

	DROP TEMPORARY TABLE IF EXISTS tmp.sale;
	CREATE TEMPORARY TABLE tmp.sale
		(PRIMARY KEY (saleFk))
		ENGINE = MEMORY
		SELECT id AS saleFk, vWarehouseFk warehouseFk
			FROM sale s WHERE s.ticketFk = vTicketFk;

	CALL ticketComponentUpdateSale (vOption);

	DROP TEMPORARY TABLE tmp.sale;
	COMMIT;
END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketComponentUpdateSale`(vCode VARCHAR(25))
BEGIN
/**
 * A partir de la tabla tmp.sale, crea los Movimientos_componentes
 * y modifica el campo Preu de la tabla Movimientos
 *
 * @param i_option integer tipo de actualizacion
 * @param table tmp.sale  tabla memory con el campo saleFk, warehouseFk
 **/
	DECLARE vComponentFk INT;

	IF vCode <> 'renewPrices' THEN 
		SELECT id INTO vComponentFk FROM component WHERE `code` = vCode;
	END IF;

	DELETE sc.* 
		FROM tmp.sale tmps 
			JOIN saleComponent sc ON sc.saleFk = tmps.saleFk
			JOIN `component` c ON c.id = sc.componentFk
	WHERE c.isRenewable;

	REPLACE INTO saleComponent(saleFk, componentFk, value)
		SELECT s.id, tc.componentFk, tc.cost
			FROM sale s 
				JOIN tmp.sale tmps ON tmps.saleFk = s.id
				JOIN tmp.ticketComponent tc ON tc.itemFk = s.itemFk AND tc.warehouseFk = tmps.warehouseFk
				LEFT JOIN saleComponent sc ON sc.saleFk = s.id
					AND sc.componentFk = tc.componentFk
				LEFT JOIN `component` c ON c.id = tc.componentFk
			WHERE IF(sc.componentFk IS NULL AND NOT c.isRenewable, FALSE, TRUE);

	-- Añadir componente venta por paquete
	REPLACE INTO saleComponent(saleFk, componentFk, value)
		SELECT t.id, t.componentFk, t.cost
			FROM (
				SELECT s.id, tc.componentFk, tc.cost, MOD(s.quantity, b.packing) as resto
					FROM vn.sale s
						JOIN tmp.sale tmps ON tmps.saleFk = s.id
						JOIN cache.last_buy lb ON lb.item_id = s.itemFk AND tmps.warehouseFk = lb.warehouse_id
						JOIN vn.buy b ON b.id = buy_id
						JOIN tmp.ticketComponent tc ON tc.itemFk = s.itemFk AND tc.warehouseFk = tmps.warehouseFk
						JOIN `component` c ON c.id = tc.componentFk AND c.code = 'salePerPackage' 
						LEFT JOIN (
							SELECT s.id
							FROM vn.sale s
								JOIN tmp.sale tmps ON tmps.saleFk = s.id
								JOIN tmp.ticketComponent tc ON tc.itemFk = s.itemFk AND tc.warehouseFk = tmps.warehouseFk
								JOIN saleComponent sc ON sc.saleFk = s.id AND sc.componentFk = tc.componentFk
								JOIN `component` c ON c.id = sc.componentFk AND c.code = 'lastUnitsDiscount'
						) tp ON tp.id = s.id
					WHERE tp.id IS NULL
					HAVING resto <> 0) t;

	IF vCode <> 'renewPrices' THEN
		REPLACE INTO saleComponent(saleFk, componentFk, value)
			SELECT s.id, vComponentFk, ROUND((s.price * (100 - s.discount) / 100) - SUM(sc.value), 3) dif
				FROM sale s
					JOIN tmp.sale tmps ON tmps.saleFk = s.id
					LEFT JOIN saleComponent sc ON sc.saleFk = s.id
			WHERE  sc.saleFk <> vComponentFk
			GROUP BY s.id
			HAVING dif <> 0;
	ELSE
		UPDATE sale s
			JOIN item i on i.id = s.itemFk
			JOIN itemType it on it.id = i.typeFk
				JOIN (SELECT SUM(sc.value) sumValue, sc.saleFk
						FROM saleComponent sc
							JOIN tmp.sale tmps ON tmps.saleFk = sc.saleFk
						GROUP BY sc.saleFk) sc ON sc.saleFk = s.id
				SET s.price = sumValue / ((100 - s.discount) / 100)
			WHERE it.code != 'PRT' ;

		REPLACE INTO saleComponent(saleFk, componentFk, value)
			SELECT s.id, 21, ROUND((s.price * (100 - s.discount) / 100) - SUM(value), 3) saleValue
				FROM sale s
					JOIN tmp.sale tmps ON tmps.saleFk = s.id
					LEFT JOIN saleComponent sc ON sc.saleFk = s.id
				WHERE sc.componentFk != 21
				GROUP BY s.id
				HAVING ROUND(saleValue, 4) <> 0;
	END IF;

	UPDATE sale s
		JOIN (
			SELECT SUM(sc.value) sumValue, sc.saleFk 
				FROM saleComponent sc
					JOIN tmp.sale tmps ON tmps.saleFk = sc.saleFk
					JOIN `component` c ON c.id = sc.componentFk
					JOIN componentType ct on ct.id = c.typeFk AND ct.isBase
				GROUP BY sc.saleFk) sc ON sc.saleFk = s.id
		SET s.priceFixed = sumValue, s.isPriceFixed = 1;

	DELETE sc.*
		FROM saleComponent sc
			JOIN tmp.sale tmps ON tmps.saleFk = sc.saleFk
			JOIN sale s on s.id = sc.saleFk
			JOIN item i ON i.id = s.itemFk
			JOIN itemType it ON it.id = i.typeFk
		WHERE it.code = 'PRT';
		
	INSERT INTO saleComponent(saleFk, componentFk, value)
		SELECT s.id, 15, s.price
			FROM sale s
				JOIN tmp.sale tmps ON tmps.saleFk = s.id
				JOIN item i ON i.id = s.itemFK
				JOIN itemType it ON it.id = i.typeFk
		WHERE it.code = 'PRT' AND s.price > 0;
END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_checkNoComponents`(vShippedFrom DATETIME, vShippedTo DATETIME)
BEGIN

/**
 * Comprueba que los tickets entre un rango de fechas tienen componentes
 * y recalcula sus componentes
 *
 * @param vShippedFrom rango inicial de fecha 
 * @param vShippedTo rango final de fecha
 */
 
	CREATE OR REPLACE TEMPORARY TABLE tmp.coste
			(primary key (id)) ENGINE = MEMORY
		SELECT s.id
		FROM ticket t
			JOIN sale s ON s.ticketFk = t.id
			JOIN item i ON i.id = s.itemFk
			JOIN itemType tp ON tp.id = i.typeFk
			JOIN itemCategory ic ON ic.id = tp.categoryFk
			JOIN saleComponent sc ON sc.saleFk = s.id
			JOIN component c ON c.id = sc.componentFk
			JOIN componentType ct ON ct.id = c.typeFk AND ct.id = 1
		WHERE t.shipped BETWEEN vShippedFrom AND vShippedTo
			AND ic.merchandise;

	CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
			(primary key (id)) ENGINE = MEMORY
		SELECT DISTINCT s.id
			FROM ticket t
				JOIN sale s ON s.ticketFk = t.id
				JOIN item i ON i.id = s.itemFk
				JOIN itemType tp ON tp.id = i.typeFk
				JOIN itemCategory ic ON ic.id = tp.categoryFk
				LEFT JOIN tmp.coste c ON c.id = s.id
			WHERE t.shipped >= vShippedFrom AND t.shipped <= vShippedTo
				AND c.id IS NULL
				AND ic.merchandise;

	CALL sale_recalcComponent('renewPrices');

	DROP TEMPORARY TABLE tmp.recalculateSales;
	DROP TEMPORARY TABLE tmp.coste;	
 END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_componentMakeUpdate`(
		vTicketFk INT,
		vClientFk INT,
		vNickname VARCHAR(50),
		vAgencyModeFk INT,
		vAddressFk INT,
		vZoneFk INT,
		vWarehouseFk INT,
		vCompanyFk INT,
		vShipped DATETIME,
		vLanded DATE,
		vIsDeleted BOOLEAN,
		vHasToBeUnrouted BOOLEAN,
		vOption VARCHAR(25))
BEGIN
	
	/**
	 * Modifica en el ticket los campos que se le pasan por parámetro
	 * y cambia sus componentes
	 * 
	 * @param vTicketFk Id del ticket a modificar
	 * @param vClientFk nuevo cliente
	 * @param vNickname nuevo alias
	 * @param vAgencyModeFk nueva agencia
	 * @param vAddressFk nuevo consignatario
	 * @param vZoneFk nueva zona
	 * @param vWarehouseFk nuevo almacen
	 * @param vCompanyFk nueva empresa
	 * @param vShipped nueva fecha del envio de mercancia
	 * @param vLanded nueva fecha de recepcion de mercancia
	 * @param vIsDeleted si se borra el ticket
	 * @param vHasToBeUnrouted si se le elimina la ruta  al ticket
	 * @param vOption opcion para el case del proc ticketComponentUpdateSale
	 */
	
	DECLARE vPrice DECIMAL(10,2);
	DECLARE vBonus DECIMAL(10,2);

	CALL ticket_componentPreview (vTicketFk, vLanded, vAddressFk, vZoneFk, vWarehouseFk);

	IF (SELECT addressFk FROM ticket WHERE id = vTicketFk) <> vAddressFk THEN
			
		UPDATE ticket t 
				JOIN address a ON a.id = vAddressFk
			SET t.nickname = a.nickname
			WHERE t.id = vTicketFk;
			
	END IF;

	CALL zone_getShipped(vLanded, vAddressFk, vAgencyModeFk, TRUE);

	SELECT zoneFk, price, bonus INTO vZoneFk, vPrice, vBonus 
		FROM tmp.zoneGetShipped
		WHERE shipped BETWEEN DATE(vShipped) AND util.dayEnd(vShipped)  AND warehouseFk = vWarehouseFk LIMIT 1;

	UPDATE ticket t
		SET 
			t.clientFk = vClientFk,
		    t.nickname = vNickname,
			t.agencyModeFk = vAgencyModeFk,
			t.addressFk = vAddressFk,
			t.zoneFk = vZoneFk,
			t.zonePrice = vPrice,
			t.zoneBonus = vBonus,
			t.warehouseFk = vWarehouseFk,
			t.companyFk = vCompanyFk,
			t.landed = vLanded,
			t.shipped = vShipped,
			t.isDeleted = vIsDeleted
		WHERE
			t.id = vTicketFk;

	IF vHasToBeUnrouted THEN
		UPDATE ticket t SET t.routeFk = NULL
			WHERE t.id = vTicketFk;
	END IF;

	DROP TEMPORARY TABLE IF EXISTS tmp.sale;
	CREATE TEMPORARY TABLE tmp.sale
		(PRIMARY KEY (saleFk))
		ENGINE = MEMORY
		SELECT id AS saleFk, vWarehouseFk warehouseFk
			FROM sale s WHERE s.ticketFk = vTicketFk;

	DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;
	CREATE TEMPORARY TABLE tmp.ticketComponent
		SELECT * FROM tmp.ticketComponentPreview;

	CALL ticketComponentUpdateSale (vOption);

	DROP TEMPORARY TABLE tmp.sale;
	DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponent;

	DROP TEMPORARY TABLE tmp.zoneGetShipped, tmp.ticketComponentPreview;
END$$
DELIMITER ;

DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_recalcComponents`(vSelf INT, vOption VARCHAR(25))
proc: BEGIN

/**
 * Crea tabla temporal para sale_recalcComponent() para recalcular los componentes
 *
 * @param vSelf Id del ticket
 * @param vOption si no se quiere forzar llamar con NULL
 */

	CREATE OR REPLACE TEMPORARY TABLE tmp.recalculateSales
		SELECT s.id
			FROM sale s
			WHERE s.ticketFk = vSelf;

	CALL sale_recalcComponent(vOption);

	DROP TEMPORARY TABLE tmp.recalculateSales;
END$$
DELIMITER ;

TRUNCATE TABLE `vn`.`ticketUpdateAction`;
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(1, 'Cambiar los precios en el ticket', 'renewPrice');
INSERT INTO `vn`.`ticketUpdateAction` (id, description, code) VALUES(2, 'Convertir en maná', 'mana');