
    E[8i                     ,   d dl mZ d dlmZ d dlmZ d dlmZmZ d dl	m
Z
 d dlmZ d dlmZ dd	lmZ dd
lmZ d dlZd dlZd dlZd dlmZ d ZdefdZdefdZddZdefdZdefdZdefdZdefdZ d Z!dede"fdZ#d Z$d Z%dede"fdZ&defdZ'y)    )login_required)render)csrf_exempt)JsonResponseHttpResponse)transaction)now)datetime   )report_exporta_data)getConnexionN)queryfox_histc            
         d} t               }	 |j                         5 }t        d       |j                  |        |j                  D cg c]  }|d   	 }}|j                         D cg c]  }t        t        ||             }}|cd d d        S c c}w c c}w # 1 sw Y   y xY w# t        $ r}g cY d }~S d }~ww xY w)NzI
            SELECT id,Number FROM Campaign ORDER BY id DESC
            zconectando..r   )	r   cursorprintexecutedescriptionfetchalldictzip	Exception)query
connectionr   columncolumnsrowresultses           4/var/www/html/planif/app/services/foxQueryService.pygetCampaignNumbersr       s    E J  
	F.!NN5! 06/A/ABVvayBGB ;A//:KL3tC-.LGL
	 
	
 C M
	 
	  	sX   B( +B	BB)BB	B( 
BB%!B( %B( (	B=1B82B=8B=campaign_idc                    t               }	 |j                         5 }d}|j                  || f       |j                         }|r!|d   cddd       |r|j	                          S S 	 ddd       |r|j	                          yy# 1 sw Y   nxY wn5# t
        $ r)}t        d|       Y d}~|r|j	                          yyd}~ww xY w	 |r|j	                          yy# |r|j	                          w w xY w)zG
    Dado un campaign_id, obtiene el campo Number (ej: '14/2025').
    z(SELECT Number FROM Campaign WHERE Id = ?r   Nz!Error obteniendo campaign_number:)r   r   r   fetchonecloser   r   )r!   r   r   sqlr   r   s         r   get_the_number_for_campaignidr&   (   s   
 J  	F<CNN3///#C1v	 	   	  	 	 	  115 		  : sR   B ,B	B %B&B BB C 	CB<$C <CC C.c                    d}t               }	 |j                         5 }|j                  || f       |j                         }ddd       sdddd|j	                          S j
                  D cg c]  }|d   	 }}t        t        ||            |j	                          S # 1 sw Y   bxY wc c}w # |j	                          w xY w)z
    Retorna un diccionario con:
      - campaign_number (N)
      - previous_campaign_id (N-1)
      - previous_campaign_number (N-1)
    a<  
        DECLARE @CCID INT = ?;

        SELECT 
            C.Number AS CampaignNumber,
            Prev.Id AS PreviousCampaignId,
            Prev.Number AS PreviousCampaignNumber
        FROM Campaign C
        LEFT JOIN Campaign Prev
            ON Prev.Sequence = C.Sequence - 1
        WHERE C.Id = @CCID;
    N)campaign_numberprevious_campaign_idprevious_campaign_numberr   )r   r   r   r#   r$   r   r   r   r!   r%   r   r   r   colcolss          r   get_n_and_n1_campaign_numbersr.   @   s    C J  	$FNN3///#C	$ #'(,,0 		 #)"4"453A55CcN# 		$ 	$ 6 	s4   B4 $B#B4 "B4 0B/<B4 #B,(B4 4Cc           
      x   t        |       }|st        d|         g S d}t               }	 |j                         5 }|j	                  || |f       |j
                  .|j                         r	 |j
                  |j                         r|j
                  &t        d       g cddd       |j                          S |j
                  D cg c]  }|d   	 }}|j                         D cg c]  }t        t        ||             }	}|r%|	r#t        d       t        |	| |       t        d       |	cddd       |j                          S c c}w c c}w # 1 sw Y   nxY wn4# t        $ r(}
t        d|
       g cY d}
~
|j                          S d}
~
ww xY w	 |j                          y# |j                          w xY w)	ur   
        Ejecuta la query Fox principal. 
        Si is_save=True → guarda los resultados en queryfox_hist.
    (   No se encontró Number para campaign_id=u?:  
        ------------------------------------------------------------
        -- PARAMS
        ------------------------------------------------------------
        DECLARE @CCID INT = ?;
        DECLARE @campana VARCHAR(10) = ?;

         -- Campaña anterior por Sequence
        DECLARE @previousCampaignId INT = (
            SELECT TOP 1 Id
            FROM Campaign
            WHERE Sequence = (SELECT Sequence - 1 FROM Campaign WHERE Id = @CCID)
        );

		SELECT DISTINCT CPL.LevelId
        INTO #TotalesZonasN 
        FROM CampaignLevel CPL
        INNER JOIN Level LV ON LV.id = CPL.LevelId
        INNER JOIN CampaignZona CZ on cz.CampaignLevelId = CPL.Id
        WHERE CPL.CampaignId = @CCID and LV.LevelTypeId = 6 --tipo zona 
        AND CZ.DateCC != '0001-01-01 00:00:00.0000000'
        
        -- Total facturado en campaña N-1 (solo computables, en UNIDADES)
        DECLARE @TotalNM1 INT = (
            SELECT SUM(OI.Quantity)
            FROM Orders O
            INNER JOIN OrderItem OI ON OI.OrderId = O.Id
            INNER JOIN CampaignProduct CP ON CP.Id = OI.CampaignProductId
            INNER JOIN ProductType PT ON PT.Id = CP.ProductTypeId
            INNER JOIN #TotalesZonasN TZN ON TZN.LevelId = o.Zone
            WHERE O.CampaignId = @previousCampaignId
            AND O.OrderStatus IN (5,6,7,8,10)
            AND OI.PaymentCycle = 1
            AND OI.IsReturn = 0
            AND OI.IsHidden = 0
            AND O.Zone NOT IN (999,998,997)
            AND OI.IsPreviousNoStock = 0
            AND PT.Id != 13
        );

        ------------------------------------------------------------
        -- ItemsNormalizados (3 bloques)
        ------------------------------------------------------------
        SELECT 
            O.Id AS OrderId,
            CAST(O.InvoicingDate AS DATE) AS Fecha,
            P.Code,
            P.Name,
            OI.PromotionPrice AS PrecioVenta,
            OI.PromotionPriceQuantity AS Cantidad,
            CASE WHEN OI.OrderItemStatus = 3 THEN OI.PromotionPriceQuantity ELSE 0 END AS NoStockCantidad,
            C.CommercialOrders,
            CP.EstimatedSales,
            PT.Id AS ProductTypeId,
            PT.Description AS ProductType,
            PC.Name       AS Division
        INTO #ItemsNormalizados
        FROM OrderItem OI
        INNER JOIN Orders O           ON O.Id = OI.OrderId
        INNER JOIN CampaignProduct CP ON CP.Id = OI.CampaignProductId
        INNER JOIN Product P          ON P.Id = CP.ProductId
        INNER JOIN ProductType PT     ON CP.ProductTypeId = PT.Id
        INNER JOIN ProductCategory PC ON P.ProductCategoryId = PC.Id
        INNER JOIN Campaign C         ON C.Id = @CCID
        WHERE O.CampaignId = @CCID
        AND O.OrderStatus IN (5,6,7,8,10)
        AND OI.PromotionPriceQuantity > 0
        AND OI.PaymentCycle = 1
        AND OI.IsReturn    = 0
        AND OI.IsHidden    = 0
        AND O.Zone NOT IN (999,998,997)
        AND OI.IsPreviousNoStock = 0
        UNION ALL
        -- Diferencia sin promo
        SELECT 
            O.Id AS OrderId,
            CAST(O.InvoicingDate AS DATE) AS Fecha,
            P.Code,
            P.Name,
            OI.Price AS PrecioVenta,
            (OI.Quantity - OI.PromotionPriceQuantity) AS Cantidad,
            CASE WHEN OI.OrderItemStatus = 3 THEN (OI.Quantity - OI.PromotionPriceQuantity) ELSE 0 END AS NoStockCantidad,
            C.CommercialOrders,
            CP.EstimatedSales,
            PT.Id AS ProductTypeId,
            PT.Description AS ProductType,
            PC.Name       AS Division
        FROM OrderItem OI
        INNER JOIN Orders O           ON O.Id = OI.OrderId
        INNER JOIN CampaignProduct CP ON CP.Id = OI.CampaignProductId
        INNER JOIN Product P          ON P.Id = CP.ProductId
        INNER JOIN ProductType PT     ON CP.ProductTypeId = PT.Id
        INNER JOIN ProductCategory PC ON P.ProductCategoryId = PC.Id
        INNER JOIN Campaign C         ON C.Id = @CCID
        WHERE O.CampaignId = @CCID
        AND O.OrderStatus IN (5,6,7,8,10)
        AND OI.PromotionPriceQuantity > 0
        AND OI.Quantity > OI.PromotionPriceQuantity
        AND OI.PaymentCycle = 1
        AND OI.IsReturn    = 0
        AND OI.IsHidden    = 0
        AND O.Zone NOT IN (999,998,997)
        AND OI.IsPreviousNoStock = 0
        UNION ALL
        -- Sin promo
        SELECT 
            O.Id AS OrderId,
            CAST(O.InvoicingDate AS DATE) AS Fecha,
            P.Code,
            P.Name,
            OI.Price AS PrecioVenta,
            OI.Quantity AS Cantidad,
            CASE WHEN OI.OrderItemStatus = 3 THEN OI.Quantity ELSE 0 END AS NoStockCantidad,
            C.CommercialOrders,
            CP.EstimatedSales,
            PT.Id AS ProductTypeId,
            PT.Description AS ProductType,
            PC.Name       AS Division
        FROM OrderItem OI
        INNER JOIN Orders O           ON O.Id = OI.OrderId
        INNER JOIN CampaignProduct CP ON CP.Id = OI.CampaignProductId
        INNER JOIN Product P          ON P.Id = CP.ProductId
        INNER JOIN ProductType PT     ON CP.ProductTypeId = PT.Id
        INNER JOIN ProductCategory PC ON P.ProductCategoryId = PC.Id
        INNER JOIN Campaign C         ON C.Id = @CCID
        WHERE O.CampaignId = @CCID
        AND O.OrderStatus IN (5,6,7,8,10)
        AND ISNULL(OI.PromotionPriceQuantity,0) = 0
        AND OI.PaymentCycle = 1
        AND OI.IsReturn    = 0
        AND OI.IsHidden    = 0
        AND O.Zone NOT IN (999,998,997)
        AND OI.IsPreviousNoStock = 0;

        ------------------------------------------------------------
        -- Agregado por producto/día (N)
        ------------------------------------------------------------
        SELECT
            F.Fecha,
            F.Code,
            F.Name,
            F.PrecioVenta,
            F.ProductType,
            F.Division,
            SUM(F.Cantidad) - SUM(F.NoStockCantidad) AS [UNI FACT X DIA_PRECIO],
            SUM(F.NoStockCantidad)                    AS [UNI CORTADAS X DIA_PRECIO],
            MAX(F.CommercialOrders)                   AS CommercialOrders,
            MAX(F.EstimatedSales)                     AS EstimatedSales
        INTO #Agregado
        FROM #ItemsNormalizados F
        GROUP BY F.Fecha, F.Code, F.Name, F.PrecioVenta, F.ProductType, F.Division;

        ------------------------------------------------------------
        -- CampaignDaily / CampaignCum / PriceCum / Proy
        ------------------------------------------------------------
        SELECT Fecha, SUM([UNI FACT X DIA_PRECIO]) AS UnitsByDay
        INTO #CampaignDaily
        FROM #Agregado
        GROUP BY Fecha;

        SELECT Fecha, SUM(UnitsByDay) OVER (ORDER BY Fecha ROWS UNBOUNDED PRECEDING) AS TotalNXDay
        INTO #CampaignCum
        FROM #CampaignDaily;

        SELECT Code, PrecioVenta, Fecha,
            SUM([UNI FACT X DIA_PRECIO]) OVER (
                PARTITION BY Code, PrecioVenta ORDER BY Fecha ROWS UNBOUNDED PRECEDING
            ) AS TotalPrice
        INTO #PriceCum
        FROM #Agregado;

        SELECT PC.Code, PC.PrecioVenta, PC.Fecha,
            CC.TotalNXDay, @TotalNM1 AS Porcentaje, PC.TotalPrice AS InvoicingUnitsProjected
        INTO #Proy
        FROM #PriceCum PC
        INNER JOIN #CampaignCum CC ON CC.Fecha = PC.Fecha;
        

        ------------------------------------------------------------
		-- CALENDARIO REAL DE FACTURACIÓN (solo días con ventas)
		------------------------------------------------------------

		SELECT
		  Fecha,
		  ROW_NUMBER() OVER (ORDER BY Fecha) AS DIA_CALENDARIO_REAL
		INTO #CalendarioDias
		FROM (
		  -- Podés basarte en #Agregado (por producto/día/precio) o en #CampaignDaily (por día)
		  SELECT DISTINCT Fecha FROM #CampaignDaily
		) D
		ORDER BY Fecha;

        ------------------------------------------------------------
        -- Facturación acumulada y NoStock (en UNIDADES)
        ------------------------------------------------------------
        SELECT Code, PrecioVenta, Fecha,
            SUM([UNI FACT X DIA_PRECIO]) OVER (
                PARTITION BY Code, PrecioVenta ORDER BY Fecha ROWS UNBOUNDED PRECEDING
            ) AS UNI_ACU_FACT_HASTA_DIA
        INTO #FactAcum
        FROM #Agregado;

        SELECT Fecha, Code, SUM([UNI CORTADAS X DIA_PRECIO]) AS UNI_CORTADAS_X_DIA
        INTO #NoStockPorDia
        FROM #Agregado
        GROUP BY Fecha, Code;

        SELECT Code, Fecha,
            SUM(UNI_CORTADAS_X_DIA) OVER (
                PARTITION BY Code ORDER BY Fecha ROWS UNBOUNDED PRECEDING
            ) AS UNI_ACU_CORTADAS_HASTA_DIA
        INTO #NoStockAcum
        FROM #NoStockPorDia;

        ------------------------------------------------------------
        -- N-1: Totales por zona (constantes)
        ------------------------------------------------------------
        SELECT 
            o.Zone AS ZonaPedido,
            SUM(oi.Quantity) AS UnidsNM1_Total
        INTO #NM1Totales
        FROM Orders o
        JOIN OrderItem oi ON oi.OrderId = o.Id
        JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
        JOIN ProductType pt ON pt.Id = cp.ProductTypeId
        WHERE o.CampaignId = @previousCampaignId
        AND o.OrderStatus IN (5,6,7,8,10)
        AND oi.PaymentCycle = 1
        AND oi.IsReturn = 0
        AND oi.IsHidden = 0
        AND o.Zone NOT IN (999,998,997)
        AND oi.IsPreviousNoStock = 0
        AND pt.Id != 13
        GROUP BY o.Zone;

        ------------------------------------------------------------
        -- Fechas de corte N y zonas de N
        ------------------------------------------------------------
        SELECT DISTINCT Fecha INTO #Cortes FROM #Agregado;

        SELECT c.Fecha, o.Zone AS ZonaPedido
        INTO #ZonasN_porDia
        FROM #Cortes c
        JOIN Orders o ON o.CampaignId = @CCID
        JOIN OrderItem oi ON oi.OrderId = o.Id
        JOIN CampaignProduct CP ON CP.Id = oi.CampaignProductId
        JOIN ProductType PT ON CP.ProductTypeId = PT.Id
        WHERE o.OrderStatus IN (5,6,7,8,10)
        AND oi.PaymentCycle = 1
        AND oi.IsReturn = 0
        AND oi.IsHidden = 0
        AND o.Zone NOT IN (999,998,997)
        AND oi.IsPreviousNoStock = 0
        AND CAST(o.InvoicingDate AS date) <= c.Fecha
        AND PT.Id != 13
        GROUP BY c.Fecha, o.Zone;

        ------------------------------------------------------------
        -- Denominador N-1: suma de totales N-1 de las zonas que ya aparecieron en N
        ------------------------------------------------------------
        SELECT zn.Fecha, SUM(t.UnidsNM1_Total) AS TotalNM1_Hoy
        INTO #TotalNM1_HoyPorDia
        FROM #ZonasN_porDia zn
        JOIN #NM1Totales t ON t.ZonaPedido = zn.ZonaPedido
        GROUP BY zn.Fecha;

        ------------------------------------------------------------
        -- Facturación N por día y acumulada
        ------------------------------------------------------------
        DECLARE @TotalFactNM1 DECIMAL(18,2) = (
            SELECT SUM(
                ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
                + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * oi.Price
            )
            FROM Orders o
            JOIN OrderItem oi ON oi.OrderId = o.Id
            JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
            JOIN ProductType pt ON pt.Id = cp.ProductTypeId
            INNER JOIN #TotalesZonasN TZN ON TZN.LevelId = o.Zone
            WHERE o.CampaignId = @previousCampaignId
            AND o.OrderStatus IN (5,6,7,8,10)
            AND oi.PaymentCycle = 1
            AND oi.IsReturn = 0
            AND oi.IsHidden = 0
            AND o.Zone NOT IN (997,998,999)
            AND oi.IsPreviousNoStock = 0
            AND pt.Id != 13
        );

        SELECT Fecha,
            SUM(PrecioVenta * [UNI FACT X DIA_PRECIO]) AS FacturacionDia
        INTO #FactN_Dia
        FROM #Agregado
        GROUP BY Fecha;

        SELECT Fecha,
            FacturacionDia,
            SUM(FacturacionDia) OVER (ORDER BY Fecha ROWS UNBOUNDED PRECEDING) AS FacturacionAcumuladaN
        INTO #FactN
        FROM #FactN_Dia;

        ------------------------------------------------------------
        -- SELECT FINAL
        ------------------------------------------------------------
        SELECT 
            RIGHT(SUBSTRING(@campana, CHARINDEX('/', @campana) + 1, 4), 2) AS AA,
            CAST(LEFT(@campana, CHARINDEX('/', @campana) - 1) AS INT) AS CC,
            DAY(A.Fecha)  AS DIA,
            MONTH(A.Fecha) AS MES,
            YEAR(A.Fecha) AS ANO,
            A.Code AS COD_PROD,
            A.Name AS DESC_PROD,
            CAST(CASE WHEN A.CommercialOrders = 0 OR A.EstimatedSales = 0 THEN 0
                    ELSE ROUND(A.EstimatedSales * 1.0 / A.CommercialOrders, 4) END AS DECIMAL(10,4)) AS ESTI_PED,
            CAST(CASE WHEN A.CommercialOrders = 0 THEN 0
                    ELSE ROUND(P.InvoicingUnitsProjected * 1.0 / A.CommercialOrders, 4) END AS DECIMAL(10,4)) AS ENTREG_PED,
            A.EstimatedSales AS UNI_EST,
            CAST((1.0 * @TotalNM1 * FA.UNI_ACU_FACT_HASTA_DIA) / NULLIF(tnm1d.TotalNM1_Hoy,0) AS INT) AS UNI_PROY_INVOICING,
            A.[UNI FACT X DIA_PRECIO] AS [UNI FACT X DIA],
            FA.UNI_ACU_FACT_HASTA_DIA,
            NSD.UNI_CORTADAS_X_DIA,
            NSA.UNI_ACU_CORTADAS_HASTA_DIA,
            CD.DIA_CALENDARIO_REAL        AS  DIA_CALENDARIO_FACT,
            A.ProductType,
            A.Division,
            A.PrecioVenta
        FROM #Agregado A
        INNER JOIN #Proy P ON P.Code = A.Code AND P.PrecioVenta = A.PrecioVenta AND P.Fecha = A.Fecha
        INNER JOIN #FactAcum FA ON FA.Code = A.Code AND FA.PrecioVenta = A.PrecioVenta AND FA.Fecha = A.Fecha
        INNER JOIN #NoStockPorDia NSD ON NSD.Code = A.Code AND NSD.Fecha = A.Fecha 
        INNER JOIN #NoStockAcum NSA ON NSA.Code = A.Code AND NSA.Fecha = A.Fecha
        INNER JOIN #TotalNM1_HoyPorDia tnm1d ON tnm1d.Fecha = A.Fecha
        INNER JOIN #CalendarioDias CD ON CD.Fecha = A.Fecha

        ORDER BY A.Fecha, DIA_CALENDARIO_FACT, A.PrecioVenta;

        ------------------------------------------------------------
        -- Limpieza de tablas temporales
        ------------------------------------------------------------
        DROP TABLE #ItemsNormalizados;
        DROP TABLE #Agregado;
        DROP TABLE #CampaignDaily;
        DROP TABLE #CampaignCum;
        DROP TABLE #PriceCum;
        DROP TABLE #Proy;
        DROP TABLE #CalendarioDias;
        DROP TABLE #FactAcum;
        DROP TABLE #NoStockPorDia;
        DROP TABLE #NoStockAcum;
        DROP TABLE #Cortes;
        DROP TABLE #ZonasN_porDia;
        DROP TABLE #TotalNM1_HoyPorDia;
        DROP TABLE #FactN_Dia;
        DROP TABLE #FactN;
        DROP TABLE #NM1Totales;
        DROP TABLE #TotalesZonasN;

        Nu$   No se encontró ningún SELECT finalr   z) Guardando resultados en queryfox_hist...z0 Datos guardados en queryfox_hist correctamente.zError:)r&   r   r   r   r   r   nextsetr$   r   r   r   insert_queryfox_histr   )r!   is_saver(   r   r   r   r,   r   r   r   r   s              r   get_queryfoxservicer4   j   s    4K@O8FG	eEN J  	FNN5;"@A $$,1A $$,1A !!)<=	 	2 	 *0););<#s1v<G<:@//:KL3tC-.LGL 7AB$Wk?KHI)	 	2 	 =L	 	 	*  h		+	2 	
s}   E! AE
E"	E! <E
E
E*E*E0	E! 

EEE!  F' !	F*F7F8F' FF' 'F9c                    t        |       }|st        d|         yd}t               }|j                         5 }|j	                  || |f       	 |j
                  n|j                         sn|j
                  t        d       	 ddd       y|j
                  D cg c]  }|d   	 }}|j                         }ddd       syt        t        |            }|j                  d      t        |d         |d<   |j                  d      t        |d         |d<   |j                  d      t        |d         |d<   |S c c}w # 1 sw Y   xY w)	uu   
    Calcula los pedidos proyectados de una campaña.
    Retorna solo el último registro (día más reciente).
    r0   Nu   
        DECLARE @CCID INT = ?;
        DECLARE @campana VARCHAR(10) = ?;
        DECLARE @previousCampaignId INT = (
            SELECT TOP 1 Id
            FROM Campaign
            WHERE Sequence = (SELECT Sequence - 1 FROM Campaign WHERE Id = @CCID)
        );

        
        ------------------------------------------------------------
        -- 1) Zonas válidas de campaña N (para acumulados comparables)
        ------------------------------------------------------------
        SELECT DISTINCT o.Zone
        INTO #ZonasN
        FROM Orders o
        WHERE o.CampaignId = @CCID
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 2) Zonas "estructurales" de campaña N (para total campaña N-1)
        ------------------------------------------------------------
        SELECT DISTINCT CPL.LevelId
        INTO #TotalesZonasN
        FROM CampaignLevel CPL
        INNER JOIN Level LV        ON LV.Id = CPL.LevelId
        INNER JOIN CampaignZona CZ ON CZ.CampaignLevelId = CPL.Id
        WHERE CPL.CampaignId = @CCID 
        AND LV.LevelTypeId = 6
        AND CZ.DateCC != '0001-01-01 00:00:00.0000000';

        ------------------------------------------------------------
        -- 3) Último día con pedidos en campaña N
        ------------------------------------------------------------
        SELECT MAX(CAST(o.InvoicingDate AS DATE)) AS Fecha
        INTO #UltimaFechaN
        FROM Orders o
        WHERE o.CampaignId = @CCID
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 4) Zonas que tuvieron pedidos en el ÚLTIMO día de N
        ------------------------------------------------------------
        SELECT DISTINCT o.Zone
        INTO #ZonasHoyN
        FROM Orders o
        JOIN #UltimaFechaN uf ON CAST(o.InvoicingDate AS DATE) = uf.Fecha
        WHERE o.CampaignId = @CCID
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 5) Serie de pedidos por día en campaña N (agregado por fecha)
        ------------------------------------------------------------
        SELECT 
            CAST(o.InvoicingDate AS DATE) AS InvoicingDate,
            CAST(COUNT(o.Id) AS BIGINT)   AS CantidadDia,
            SUM(CAST(COUNT(o.Id) AS BIGINT)) 
                OVER (ORDER BY CAST(o.InvoicingDate AS DATE)) AS Acumulado
        INTO #N
        FROM Orders o
        WHERE o.CampaignId = @CCID
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999)
        GROUP BY CAST(o.InvoicingDate AS DATE);

        ------------------------------------------------------------
        -- 6) Total pedidos N-1 (solo zonas que existen en N)
        ------------------------------------------------------------
        SELECT 
            CAST(COUNT(o.Id) AS BIGINT) AS TotalPedidos
        INTO #TotalesN1
        FROM Orders o
        JOIN #ZonasN zn ON zn.Zone = o.Zone
        WHERE o.CampaignId = @previousCampaignId
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 7) Total pedidos N-1 (todas las zonas estructurales válidas)
        ------------------------------------------------------------
        SELECT 
            CAST(COUNT(o.Id) AS BIGINT) AS TotalPedidosNM1
        INTO #TotalesCampanaN1
        FROM Orders o
        JOIN #TotalesZonasN TZN ON TZN.LevelId = o.Zone
        WHERE o.CampaignId = @previousCampaignId
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 8) AcumuladoActualNM1 = total N-1 en zonas que existen en N
        --    (mismo valor que #TotalesN1, pero con nombre semántico)
        ------------------------------------------------------------
        SELECT 
            CAST(COUNT(o.Id) AS BIGINT) AS AcumuladoActualNM1
        INTO #AcumuladoN1Total
        FROM Orders o
        JOIN #ZonasN zn ON zn.Zone = o.Zone
        WHERE o.CampaignId = @previousCampaignId
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 9) AcumuladoDiaNM1 = pedidos totales en N-1
        --    de las zonas que hoy (último día de N) estuvieron activas
        ------------------------------------------------------------
        SELECT 
            CAST(COUNT(o.Id) AS BIGINT) AS AcumuladoDiaNM1
        INTO #TotalesDiaNM1
        FROM Orders o
        JOIN #ZonasHoyN zh ON zh.Zone = o.Zone
        WHERE o.CampaignId = @previousCampaignId
        AND o.OrderStatus IN (5,6,7,8,10)
        AND o.Zone NOT IN (997, 998, 999);

        ------------------------------------------------------------
        -- 10) Tomamos solo el registro del ÚLTIMO día de N
        ------------------------------------------------------------
        SELECT
            n.InvoicingDate,
            n.CantidadDia  AS CantidadActual,
            n.Acumulado    AS AcumuladoActual
        INTO #N_UltimoDia
        FROM #N n
        JOIN #UltimaFechaN uf ON n.InvoicingDate = uf.Fecha;


        -- TESTING -- 

        -- Pedidos zonas de hoy (N)

        --SELECT 
        --    o.Zone AS Zona,
        --    COUNT(o.Id) AS PedidosZonaN
        --FROM Orders o
        --JOIN #UltimaFechaN uf 
        --      ON CAST(o.InvoicingDate AS DATE) = uf.Fecha
        -- WHERE o.CampaignId = @CCID      -- Campaña N
        -- AND o.OrderStatus IN (5,6,7,8,10)
        --  AND o.Zone NOT IN (997,998,999)
        -- GROUP BY o.Zone
        -- ORDER BY o.Zone;


        -- Pedidos zonas de hoy (N-1)
        --SELECT 
        --    o.Zone AS Zona,
        --    COUNT(o.Id) AS PedidosZonaN1
        --FROM Orders o
        --JOIN #ZonasHoyN zh ON zh.Zone = o.Zone    -- ⭐ SOLO zonas activas hoy en N
        --WHERE o.CampaignId = @previousCampaignId  -- Campaña N-1
        --  AND o.OrderStatus IN (5,6,7,8,10)
        --  AND o.Zone NOT IN (997,998,999)
        --GROUP BY o.Zone
        --ORDER BY o.Zone;


        ------------------------------------------------------------
        -- 11) Resultado + Proyección
        --     Proyección = TotalPedidosNM1 * AcumuladoActual / AcumuladoActualNM1
        ------------------------------------------------------------
        SELECT
            @campana AS Campaña,
            (SELECT InvoicingDate FROM #N_UltimoDia) AS InvoicingDate,
            ISNULL((SELECT CantidadActual FROM #N_UltimoDia), 0) AS CantidadActual,
            ISNULL((SELECT CantidadActual FROM #N_UltimoDia), 0) AS AcumuladoDia,  -- "día actual"
            ISNULL((SELECT AcumuladoActual FROM #N_UltimoDia), 0) AS AcumuladoActual,
            ISNULL((SELECT AcumuladoActualNM1 FROM #AcumuladoN1Total), 0) 
                AS AcumuladoActualNM1,
            ISNULL((SELECT AcumuladoDiaNM1 FROM #TotalesDiaNM1), 0) 
                AS AcumuladoDiaNM1,
            ISNULL((SELECT TotalPedidos     FROM #TotalesN1),     0) AS TotalPedidosN,
            ISNULL((SELECT TotalPedidosNM1  FROM #TotalesCampanaN1), 0) AS TotalPedidosNM1,
            ISNULL(
                CAST(
                    (
                        ISNULL((SELECT TotalPedidosNM1 FROM #TotalesCampanaN1), 0)
                        *
                        ISNULL((SELECT AcumuladoActual FROM #N_UltimoDia), 0)
                    )
                    /
                    NULLIF(
                        ISNULL((SELECT AcumuladoActualNM1 FROM #AcumuladoN1Total), 0),
                    0)
                AS INT),
            0) AS Proyeccion;

        ------------------------------------------------------------
        -- 12) Limpieza de tablas temporales
        ------------------------------------------------------------
        DROP TABLE #ZonasN;
        DROP TABLE #TotalesZonasN;
        DROP TABLE #UltimaFechaN;
        DROP TABLE #ZonasHoyN;
        DROP TABLE #N;
        DROP TABLE #TotalesN1;
        DROP TABLE #TotalesCampanaN1;
        DROP TABLE #AcumuladoN1Total;
        DROP TABLE #TotalesDiaNM1;
        DROP TABLE #N_UltimoDia;


        u+   ⚠️ La consulta no devolvió resultados.r   
Proyeccionu   TotalCampañaN1AcumuladoActual)r&   r   r   r   r   r   r1   r#   r   r   getint)	r!   r(   r%   r   r   r,   r-   r   results	            r   get_projected_ordersr;     sv    4K@O8FGMC^ J				  s[/:; !!->>#	  %?@    #)"4"453A55oo " #dC.!F zz,+"6,#78| zz#$0$'/@(A$B !zz#$0$'/@(A$B !M% 6   s$   AEEE)EEEc                 $   d}t               }|j                         5 }|j                  || f       |j                         }ddd       sdddS j                  D cg c]  }|d   	 }}t        t        ||            S # 1 sw Y   AxY wc c}w )um   
    Trae EstimatedOrders de la campaña actual (N)
    y EstimatedOrders de la campaña anterior (N-1).
    a  
        DECLARE @CCID INT = ?;

        DECLARE @previousCampaignId INT = (
            SELECT TOP 1 Id
            FROM Campaign
            WHERE Sequence = (SELECT Sequence - 1 FROM Campaign WHERE Id = @CCID)
        );

        SELECT 
            C.EstimatedOrders,
            Cprev.EstimatedOrders AS EstimatedOrdersN1
        FROM Campaign C
        LEFT JOIN Campaign Cprev ON Cprev.Id = @previousCampaignId
        WHERE C.Id = @CCID;
    N)EstimatedOrdersEstimatedOrdersN1r   )r   r   r   r#   r   r   r   r+   s          r   get_estimated_ordersr?     s    C" J				  s[N+oo  #'dCC$001sCF1D1D#    2s   $BBB
c           
      D   t        |       }|st        d|         g S d}t               }	 |j                         5 }|j	                  || |f       |j
                  .|j                         r	 |j
                  |j                         r|j
                  &t        d       g cddd       |j                          S |j
                  D cg c]  }|d   	 }}|j                         }|D cg c]  }t        t        ||             }	}t        d       |	cddd       |j                          S c c}w c c}w # 1 sw Y   nxY wn4# t        $ r(}
t        d|
       g cY d}
~
|j                          S d}
~
ww xY w	 |j                          y# |j                          w xY w)u   
    Devuelve las unidades del día, acumuladas y proyectadas para la campaña actual (N) y la anterior (N-1).
    Usa tablas temporales y es compatible con ejecución en conjunto con otras queries tipo 'query fox'.
    /   ⚠️ No se encontró Number para campaign_id=uQ  
    ------------------------------------------------------------
    -- Variables base
    ------------------------------------------------------------
    DECLARE @CCID INT = ?;
    DECLARE @campana VARCHAR(10) = ?;

    DECLARE @previousCampaignId INT = (
        SELECT TOP 1 Id
        FROM Campaign
        WHERE Sequence = (SELECT Sequence - 1 FROM Campaign WHERE Id = @CCID)
    );


     ------------------------------------------------------------
-- 1) Zonas que existen en campaña N  (base para todo)
------------------------------------------------------------
	SELECT DISTINCT o.Zone
	INTO #ZonasN
	FROM Orders o
	WHERE o.CampaignId = @CCID
	  AND o.OrderStatus IN (5,6,7,8,10)
	  AND o.Zone NOT IN (997,998,999);


    ------------------------------------------------------------
    -- 2) Índice de días de N y N-1 (para alinear día N con día N-1)
    ------------------------------------------------------------
    SELECT DISTINCT
        CAST(o.InvoicingDate AS DATE) AS Fecha,
        DENSE_RANK() OVER (ORDER BY CAST(o.InvoicingDate AS DATE)) AS DiaIdx
    INTO #DiasN
    FROM Orders o
    WHERE o.CampaignId = @CCID
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999);



	--- nuevo --

	SELECT MAX(CAST(InvoicingDate AS DATE)) AS Fecha
	INTO #UltimaFechaN
	FROM Orders
	WHERE CampaignId = @CCID
	  AND OrderStatus IN (5,6,7,8,10)
	  AND Zone NOT IN (997,998,999);

    ------------------------------------------------------------
    -- 4) UnitsOfDay (N) = Unidades del último día de N
    ------------------------------------------------------------
   SELECT CAST(SUM(oi.Quantity) AS BIGINT) AS UnitsOfDay
		INTO #UnitsDiaN
		FROM Orders o
		JOIN #UltimaFechaN uf
			ON CAST(o.InvoicingDate AS DATE) = uf.Fecha   -- <== ESTE FILTRO
		JOIN OrderItem oi ON oi.OrderId = o.Id
		JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
		JOIN ProductType pt ON pt.Id = cp.ProductTypeId
		WHERE o.CampaignId = @CCID
		  AND o.OrderStatus IN (5,6,7,8,10)
		  AND o.Zone NOT IN (997,998,999)
		  AND oi.PaymentCycle = 1
		  AND oi.IsReturn = 0
		  AND oi.IsHidden = 0
		  AND oi.IsPreviousNoStock = 0
		  AND pt.Id != 13;

	--select * from #UnitsDiaN

	-------------------------------------------------------------
	-- nuevo zonas que facturaron hoy.
	--------------------------------------------------------------

	SELECT DISTINCT o.Zone
	INTO #ZonasHoyN
	FROM Orders o
	JOIN #UltimaFechaN uf
		ON CAST(o.InvoicingDate AS DATE) = uf.Fecha
	WHERE o.CampaignId = @CCID
	  AND o.OrderStatus IN (5,6,7,8,10)
	  AND o.Zone NOT IN (997,998,999);

  --SELECT * FROM #ZonasHoyN;
    ------------------------------------------------------------
    -- 5) UnitsOfDayNM1 (N-1) = Unidades del día equivalente,
    --    solo en zonas que existen en N
    ------------------------------------------------------------
   SELECT CAST(SUM(oi.Quantity) AS BIGINT) AS UnitsOfDayNM1
		INTO #UnitsDiaN1
		FROM Orders o
		JOIN #ZonasHoyN zh ON zh.Zone = o.Zone          -- << NO #ZonasN, SOLO ZONAS DE HOY
		JOIN OrderItem oi ON oi.OrderId = o.Id
		JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
		JOIN ProductType pt ON pt.Id = cp.ProductTypeId
		WHERE o.CampaignId = @previousCampaignId
		  AND o.OrderStatus IN (5,6,7,8,10)
		  AND o.Zone NOT IN (997,998,999)
		  AND oi.PaymentCycle = 1
		  AND oi.IsReturn = 0
		  AND oi.IsHidden = 0
		  AND oi.IsPreviousNoStock = 0
		  AND pt.Id != 13;

	--select * from #UnitsDiaN1
    ------------------------------------------------------------
    -- 6) UnitsAccumulatedN = Unidades acumuladas de N (todas las fechas)
    ------------------------------------------------------------
    SELECT CAST(SUM(oi.Quantity) AS BIGINT) AS UnitsAccumulatedN
		INTO #UnitsN
		FROM Orders o
		JOIN OrderItem oi ON oi.OrderId = o.Id
		JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
		JOIN ProductType pt ON pt.Id = cp.ProductTypeId
		WHERE o.CampaignId = @CCID
		AND o.OrderStatus IN (5,6,7,8,10)
		AND o.Zone NOT IN (997,998,999)
		AND oi.PaymentCycle = 1
		AND oi.IsReturn = 0
		AND oi.IsHidden = 0
		AND oi.IsPreviousNoStock = 0
		AND pt.Id != 13;

    ------------------------------------------------------------
    -- 7) UnitsAccumulatedN1 = Unidades acumuladas de N-1,
    --    solo para zonas que existen en N
    ------------------------------------------------------------
    SELECT CAST(SUM(oi.Quantity) AS BIGINT) AS UnitsAccumulatedN1
    INTO #UnitsN1
    FROM Orders o
    JOIN #ZonasN zn ON zn.Zone = o.Zone
    JOIN OrderItem oi ON oi.OrderId = o.Id
    JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
    JOIN ProductType pt ON pt.Id = cp.ProductTypeId
    WHERE o.CampaignId = @previousCampaignId
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999)
    AND oi.PaymentCycle = 1
    AND oi.IsReturn = 0
    AND oi.IsHidden = 0
    AND oi.IsPreviousNoStock = 0
    AND pt.Id != 13;

    ------------------------------------------------------------
    -- 8) TotalUnidadesNM1 = Total de la campaña N-1 (todas las zonas válidas)
    --    (igual que "TotalPedidosNM1" en pedidos)
    ------------------------------------------------------------
    SELECT CAST(SUM(oi.Quantity) AS BIGINT) AS TotalUnidadesNM1
    INTO #TotalesCampanaUnidadesN1
    FROM Orders o
    JOIN OrderItem oi ON oi.OrderId = o.Id
    JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
    JOIN ProductType pt ON pt.Id = cp.ProductTypeId
    WHERE o.CampaignId = @previousCampaignId
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999)
    AND oi.PaymentCycle = 1
    AND oi.IsReturn = 0
    AND oi.IsHidden = 0
    AND oi.IsPreviousNoStock = 0
    AND pt.Id != 13;

    ------------------------------------------------------------
    -- 9) Resultado + Proyección de unidades
    --    Proyección = TotalUnidadesNM1 * UnitsAccumulatedN / UnitsAccumulatedN1
    ------------------------------------------------------------
    SELECT 
        @campana AS Campaña,
        ISNULL((SELECT UnitsOfDay FROM #UnitsDiaN), 0) AS UnitsOfDay,
		ISNULL((SELECT UnitsOfDayNM1 FROM #UnitsDiaN1), 0) AS UnitsOfDayNM1,
		ISNULL((SELECT UnitsAccumulatedN  FROM #UnitsN), 0) AS UnitsAccumulatedN,
		ISNULL((SELECT UnitsAccumulatedN1 FROM #UnitsN1), 0) AS UnitsAccumulatedN1,
		ISNULL((SELECT TotalUnidadesNM1   FROM #TotalesCampanaUnidadesN1), 0) AS TotalUnidadesNM1,
		ISNULL(
			(
				(SELECT TotalUnidadesNM1 FROM #TotalesCampanaUnidadesN1) 
				* (SELECT UnitsAccumulatedN FROM #UnitsN)
			)
			/ NULLIF((SELECT UnitsAccumulatedN1 FROM #UnitsN1), 0)
		, 0) AS ProjectedUnits


    ------------------------------------------------------------
    -- 10) Limpieza
    ------------------------------------------------------------
    DROP TABLE #ZonasN;
    DROP TABLE #DiasN;
    DROP TABLE #UnitsDiaN;
    DROP TABLE #UnitsDiaN1;
    DROP TABLE #UnitsN;
    DROP TABLE #UnitsN1;
    DROP TABLE #TotalesCampanaUnidadesN1;
	drop table #UltimaFechaN;
	DROP TABLE #ZonasHoyN;

    Nu0    No se encontró ningún SELECT final con datos.r   z$ Resultados obtenidos correctamente.z  Error en get_accumulated_units:)r&   r   r   r   r   r   r1   r$   r   r   r   r   )r!   r(   r   r   r   r,   r   rowsr   r   r   s              r   get_accumulated_unitsrC      s    4K@O?}MN	CEJ J  	FNN5;"@A $$,1A $$,1A !!)HI	 	* 	 *0););<#s1v<G<??$D:>?3tC-.?G?89	 	* 	 =?	 	 	"  0!4		#	* 	
s}   E AD:
D:"	E <D:
D0D:,D5D:	E 0
D::E?E F 	E8E3E8F 3E88F Fc           
      2   t        |       }|st        d|         g S d}t               }|j                         5 }|j	                  || |f       |j
                  .|j                         r	 |j
                  |j                         r|j
                  t        d       g cddd       S |j
                  D cg c]  }|d   	 }}|j                         }|D cg c]  }t        t        ||             c}cddd       S c c}w c c}w # 1 sw Y   yxY w)u   
    Calcula la facturación del día, acumulada y proyectada
    para la campaña actual (N) y la anterior (N-1),
    excluyendo productos NO COMPUTABLES (pt.Id != 13).
    rA   u"  
    ------------------------------------------------------------
    -- Variables base
    ------------------------------------------------------------
    DECLARE @CCID INT = ?;
    DECLARE @campana VARCHAR(10) = ?;

    DECLARE @previousCampaignId INT = (
        SELECT TOP 1 Id
        FROM Campaign
        WHERE Sequence = (SELECT Sequence - 1 FROM Campaign WHERE Id = @CCID)
    );

     ------------------------------------------------------------
    -- 1) Zonas activas en campaña N (para filtrar)
    ------------------------------------------------------------
    SELECT DISTINCT o.Zone
    INTO #ZonasN
    FROM Orders o
    WHERE o.CampaignId = @CCID
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999);

 
   
    ------------------------------------------------------------
    -- 2) Último día de N 
    ------------------------------------------------------------
    SELECT MAX(CAST(InvoicingDate AS DATE)) AS Fecha
		INTO #UltimaFechaN
		FROM Orders
		WHERE CampaignId = @CCID
		  AND OrderStatus IN (5,6,7,8,10)
		  AND Zone NOT IN (997,998,999);


SELECT DISTINCT o.Zone
INTO #ZonasHoyN
FROM Orders o
JOIN #UltimaFechaN uf ON CAST(o.InvoicingDate AS DATE) = uf.Fecha
WHERE o.CampaignId = @CCID
  AND o.OrderStatus IN (5,6,7,8,10)
  AND o.Zone NOT IN (997,998,999);


    ------------------------------------------------------------
    -- 3) Facturación del día actual (N)
    ------------------------------------------------------------
    SELECT ISNULL(CAST(SUM(
        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
        + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
    ) AS DECIMAL(18,2)), 0) AS InvoicingOfDay
    INTO #FactDiaN
    FROM Orders o
	JOIN #UltimaFechaN uf ON CAST(o.InvoicingDate AS DATE) = uf.Fecha 
    JOIN OrderItem oi ON oi.OrderId = o.Id
    JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
    JOIN ProductType pt ON pt.Id = cp.ProductTypeId
    WHERE o.CampaignId = @CCID
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999)
    AND oi.PaymentCycle = 1
    AND oi.IsReturn = 0
    AND oi.IsHidden = 0
    AND oi.IsPreviousNoStock = 0
    AND pt.Id != 13;

	--SELECT * FROM #FactDiaN
   ------------------------------------------------------------
	-- 4) Facturación en N-1 de las zonas que facturaron HOY en N
	--    (toda la campaña N-1, sin filtro por fecha)
	------------------------------------------------------------
    SELECT ISNULL(CAST(SUM(
        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
        + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
    ) AS DECIMAL(18,2)), 0) AS InvoicingOfDayN1
		INTO #FactDiaN1
		FROM Orders o
		JOIN #ZonasHoyN zh ON zh.Zone = o.Zone
		JOIN OrderItem oi ON oi.OrderId = o.Id
		JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
		JOIN ProductType pt ON pt.Id = cp.ProductTypeId
		WHERE o.CampaignId = @previousCampaignId
		  AND o.OrderStatus IN (5,6,7,8,10)
		  AND o.Zone NOT IN (997,998,999)
		  AND oi.PaymentCycle = 1
		  AND oi.IsReturn = 0
		  AND oi.IsHidden = 0
		  AND oi.IsPreviousNoStock = 0
		  AND pt.Id != 13;

		  --SELECT * FROM #FactDiaN1
    ------------------------------------------------------------
    -- 5) Facturación acumulada N
    ------------------------------------------------------------
    SELECT CAST(SUM(
        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
        + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
    ) AS DECIMAL(18,2)) AS InvoicingAccumulatedN
    INTO #FactN
    FROM Orders o
    JOIN OrderItem oi ON oi.OrderId = o.Id
    JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
    JOIN ProductType pt ON pt.Id = cp.ProductTypeId
    WHERE o.CampaignId = @CCID
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999)
    AND oi.PaymentCycle = 1
    AND oi.IsReturn = 0
    AND oi.IsHidden = 0
    AND oi.IsPreviousNoStock = 0
    AND pt.Id != 13;

    ------------------------------------------------------------
    -- 6) Facturación acumulada N-1 (solo zonas válidas de N)
    ------------------------------------------------------------
    SELECT CAST(SUM(
        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
        + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
    ) AS DECIMAL(18,2)) AS InvoicingAccumulatedN1
    INTO #FactN1
    FROM Orders o
    JOIN #ZonasN zn ON zn.Zone = o.Zone
    JOIN OrderItem oi ON oi.OrderId = o.Id
    JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
    JOIN ProductType pt ON pt.Id = cp.ProductTypeId
    WHERE o.CampaignId = @previousCampaignId
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999)
    AND oi.PaymentCycle = 1
    AND oi.IsReturn = 0
    AND oi.IsHidden = 0
    AND oi.IsPreviousNoStock = 0
    AND pt.Id != 13;

    ------------------------------------------------------------
    -- 7) Total Facturación N-1 (todas las zonas válidas)
    ------------------------------------------------------------
    SELECT CAST(SUM(
        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
        + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
    ) AS DECIMAL(18,2)) AS TotalFacturacionN1
    INTO #TotalesFacturacionN1
    FROM Orders o
    JOIN OrderItem oi ON oi.OrderId = o.Id
    JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
    JOIN ProductType pt ON pt.Id = cp.ProductTypeId
    WHERE o.CampaignId = @previousCampaignId
    AND o.OrderStatus IN (5,6,7,8,10)
    AND o.Zone NOT IN (997,998,999)
    AND oi.PaymentCycle = 1
    AND oi.IsReturn = 0
    AND oi.IsHidden = 0
    AND oi.IsPreviousNoStock = 0
    AND pt.Id != 13;


	-- testing --

-- Mostrar zonas + facturación del día de hoy
--SELECT 
--    uf.Fecha AS FechaHoy,
--    o.Zone,
--    SUM(
--        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
--        + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
--    ) AS FacturacionZona
-- FROM Orders o
-- JOIN #UltimaFechaN uf 
--       ON CAST(o.InvoicingDate AS DATE) = uf.Fecha
--JOIN OrderItem oi 
--      ON oi.OrderId = o.Id
--JOIN CampaignProduct cp 
--       ON cp.Id = oi.CampaignProductId
--JOIN ProductType pt 
--       ON pt.Id = cp.ProductTypeId
--WHERE o.CampaignId = @CCID
--  AND o.OrderStatus IN (5,6,7,8,10)
--  AND o.Zone NOT IN (997,998,999)
--  AND oi.PaymentCycle = 1
--  AND oi.IsReturn = 0
--  AND oi.IsHidden = 0
--  AND oi.IsPreviousNoStock = 0
--  AND pt.Id != 13
--GROUP BY 
--    uf.Fecha,
--    o.Zone
--ORDER BY 
--    FacturacionZona DESC;

	-- testing --

--	SELECT 
--    o.Zone,
--    SUM(
--        ISNULL(oi.PromotionPriceQuantity,0) * ISNULL(oi.PromotionPrice,0)
--       + (oi.Quantity - ISNULL(oi.PromotionPriceQuantity,0)) * ISNULL(oi.Price,0)
--    ) AS FacturacionZonaN1
--FROM Orders o
--JOIN #ZonasHoyN zh ON zh.Zone = o.Zone  -- << SOLO ZONAS DE HOY EN N
--JOIN OrderItem oi ON oi.OrderId = o.Id
--JOIN CampaignProduct cp ON cp.Id = oi.CampaignProductId
--JOIN ProductType pt ON pt.Id = cp.ProductTypeId
--WHERE o.CampaignId = @previousCampaignId      -- Campaña N-1
--  AND o.OrderStatus IN (5,6,7,8,10)
--  AND o.Zone NOT IN (997,998,999)
--  AND oi.PaymentCycle = 1
--  AND oi.IsReturn = 0
--  AND oi.IsHidden = 0
--  AND oi.IsPreviousNoStock = 0
--  AND pt.Id != 13
-- GROUP BY o.Zone
-- ORDER BY o.Zone;

    ------------------------------------------------------------
    -- 8) Resultado y proyección
    ------------------------------------------------------------
    SELECT 
    @campana AS Campaña,
    ISNULL((SELECT InvoicingOfDay FROM #FactDiaN), 0) AS FacturacionDia,
    ISNULL((SELECT InvoicingOfDayN1 FROM #FactDiaN1), 0) AS FacturacionDiaN1,
	ISNULL((SELECT InvoicingAccumulatedN FROM #FactN), 0) 
        AS FacturacionAcumuladaN,
    ISNULL((SELECT InvoicingAccumulatedN1 FROM #FactN1), 0) 
        AS FacturacionAcumuladaN1_Comparable,
    ISNULL((SELECT TotalFacturacionN1 FROM #TotalesFacturacionN1), 0) 
        AS TotalFacturacionN1,
    ISNULL(
        CAST(
            (
                ISNULL((SELECT TotalFacturacionN1 FROM #TotalesFacturacionN1), 0)
                *
                ISNULL((SELECT InvoicingAccumulatedN FROM #FactN), 0)
            )
            /
            NULLIF(
                ISNULL((SELECT InvoicingAccumulatedN1 FROM #FactN1), 0), 
            0)
        AS DECIMAL(18,2)),
    0) AS FacturacionProyectada;

    ------------------------------------------------------------
    -- Limpieza
    ------------------------------------------------------------
    DROP TABLE #ZonasN;
    DROP TABLE #FactDiaN;
    DROP TABLE #FactDiaN1;
    DROP TABLE #FactN;
    DROP TABLE #FactN1;
    DROP TABLE #TotalesFacturacionN1;
	DROP TABLE #UltimaFechaN;
	DROP TABLE #ZonasHoyN;
    Nu&   ⚠️ Sin resultados de facturación.r   )
r&   r   r   r   r   r   r1   r   r   r   )	r!   r(   r   r   r   r,   r   rB   r   s	            r   get_accumulated_invoicingrE     s    4K@O?}MN	|E| J				 	9u{O<=  (V^^-=   (V^^-=%:;	9 	9 &,%7%78c3q688 378CS#&'8	9 	9 98	9 	9s6   AD	D+D9DDD7D
DDc                  8   t               } t               j                  d      }d}	 | j                         5 }|j	                  |       |j                         }|s%t        d       	 ddd       | j                          y|d   |d   |d   |d   |d	   |d
   d}|cddd       | j                          S # 1 sw Y   nxY wn2# t        $ r&}t        d|       Y d}~| j                          yd}~ww xY w	 | j                          y# | j                          w xY w)uy   
    Devuelve la campaña activa según la fecha actual.
    Busca en la tabla Campaign usando la conexión externa.
    z%Y-%m-%dz
        SELECT TOP 1 Id, Number, StartDatePick, EndDateCC, EndDatePick, Sequence
        FROM Campaign
        WHERE CAST(GETDATE() AS date) BETWEEN CAST(StartDatePick AS date) AND CAST(EndDatePick AS date)
        ORDER BY Sequence DESC
    u)    No se encontró ninguna campaña activa.Nr      r            )IdNumberStartDatePick	EndDateCCEndDatePickSequencez Error en get_current_campaign:)	r   r	   strftimer   r   r#   r   r$   r   )r   todayr   r   r   campaignr   s          r   get_current_campaignrT   (  s    
 JENN:&EE  	FNN5!//#CAB	, 	 !fa&!$Q V"1vFH !	 	, 	-	 	 	$  /3	%	, 	
sR   C 0B6'C  B6	C 6B?;C D 	C2C-D -C22D Dreturnc                 R   d}d}|rd| d}nd}t               }	 |j                         5 }|j                  || f       |j                         }|s(t	        d|         	 ddd       |j                          yt        |d         }|rt	        d	|  d
       nt	        d|  d
       |cddd       |j                          S # 1 sw Y   nxY wn2# t        $ r&}t	        d|       Y d}~|j                          yd}~ww xY w	 |j                          y# |j                          w xY w)u   
    Valida en DB si HOY (según hora de Argentina) es el EndDateCC
    de la campaña con Id = campaign_id.
    Usa SYSDATETIMEOFFSET() con AT TIME ZONE para evitar diferencias horarias.
    z
2025-10-28FzT
            SELECT CASE 
                     WHEN CAST(EndDateCC AS date) = CAST('z' AS date)
                     THEN 1 ELSE 0 
                   END AS IsLastDay
            FROM Campaign
            WHERE Id = ?
        aH  
            SELECT CASE 
                     WHEN CAST(EndDateCC AS date) = 
                          CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'UTC' AT TIME ZONE 'Argentina Standard Time' AS date)
                     THEN 1 ELSE 0 
                   END AS IsLastDay
            FROM Campaign
            WHERE Id = ?
        zNo existe Campaign.Id = Nr   z3Hoy (hora Argentina) es EndDateCC para Campaign.Id=.z7 Hoy (hora Argentina) NO es EndDateCC para Campaign.Id=z% Error en is_last_day_of_campaign_id:)r   r   r   r#   r   r$   boolr   )	r!   forced_todayuse_forced_dater%   conncurr   is_lastr   s	            r   is_last_day_of_campaign_idr^   P  s4     LO ;;G. I	 >D[[] 	cKKk^,,,.C0>?	$ 	

 3q6lGKK=XYZ[OP[}\]^_	 	$ 	

%	 	 	  5q9

		$ 	



sR   C 5C C 90C)	C CC D 	C?C:%D :C??D D&c                      d}t        | |       y)NT)r4   )r!   r3   s     r   save_foxquery_servicer`     s    GW-    c                    	 t        |      r.t        j                  j                  |      j	                          | syt        j                         5  | D cg c]  }t        d&i d|d|d|j                  d      d|j                  d      d|j                  d      d|j                  d      d	|j                  d	      d
|j                  d
      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  dd      d|j                  d      d|j                  d      d|j                  d      d|j                  d      d|j                  dd      d|j                  dd      d|j                  dd      d|j                  dd      d|j                  d      d|j                  d      d|j                  d      d |j                  d!       }}t        j                  j                  |d"#       d$d$d$       y%c c}w # 1 sw Y   y%xY w# t        $ r
}Y d$}~yd$}~ww xY w)'zR
    Inserta los resultados de get_queryfoxservice en la tabla queryfox_hist.
    
CampaignIdFrd   CampaignNumberAACCDIAMESANOCOD_PROD	DESC_PRODESTI_PED
ENTREG_PEDUNI_ESTUNI_PROY_INVOICINGPORC_DESCUENTOr   UNI_FACT_X_DIAUNI FACT X DIAUNI_ACU_FACT_HASTA_DIAUNI_CORTADAS_X_DIAUNI_ACU_CORTADAS_HASTA_DIAANT_DIAANT_ACUM	ANT_NODIA
ANT_NOACUMDIA_CALENDARIO_FACT	TIPO_PRODProductTypeDIVISION_DE_NEGOCIODivisionPRECIO_VENTA_SIN_DESCUENTOPrecioVentai  )
batch_sizeNT )
check_closed_campaignr   objectsfilterdeleter   atomicr8   bulk_creater   )r   r!   r(   r   	bulk_objsr   s         r   r2   r2     s   4 -!!((K(@GGI  ! !	I< #;: 9  *#2 wwt} wwt}	
    !WWZ0 "ggk2 !WWZ0  #ww|4  GGI. (+ww/C'D $'77+;Q#? $'77+;#<  ,/773K+L!" (+ww/C'D#$ 03ww7S/T%&  GGIq1'( !WWZ3)* "ggk15+,  #ww|Q7-. ),0E(F/0 "ggm412 ),
(;34 03ww}/E5I @ !!--iC-HC!	IH G!	IH  sG   ;I! I! IGI$#II! III! I! !	I4/I4c                     t         j                  j                  |       j                         }|rt	        d|  d       |S t	        d|  d       |S )u   
    Verifica si una campaña ya fue procesada y almacenada en queryfox_hist.
    Retorna True si existen registros, False si no.
    rc   u   La campaña z* ya fue cerrada (existe en queryfox_hist).u    La campaña u    aún no fue cerrada.)r   r   r   existsr   )r!   r   s     r   r   r     s[    
 ""))[)AHHJF[M)STU M 	k]*?@AMra   c                    	 t         j                  j                  |       j                         }|sg S g }|D ]  }i d|d   d|d   d|d   d|d   d|d   d|d   d|d   d	|d	   t	        |d	         ndd|d   t	        |d         ndd|d   d|d   d|d   d|d   d|d   d|d   d|d   d|d   |d   |d   t	        |d         ndd}|j                  |        |S # t        $ r}g cY d
}~S d
}~ww xY w)u   
    Devuelve los registros de queryfox_hist para una campaña, 
    adaptados con las mismas claves que espera el exportador Excel.
    rc   rf   rg   rh   ri   rj   rk   rl   rm   Nr   rn   ro   rp   rs   rr   rt   ru   rv   r{   r}   r|   r~   r   )r   r   )r   r   r   valuesfloatappendr   )r!   	registros
data_excelritemr   s         r   get_queryfoxservice_histr     s   
$!))00K0HOOQ	I
 	$Aagag qx qx	
 qx AjM Q{^ AjM4ME!J-0ST ,8SeAlO4YZ 1Y< %a(<&= !!$4"5 )!,D*E %a(<&= -a0L.M  &q)>'?!" q~#$ 34IJKgIhItuQ'C%DEz{'D* d#-	$2  	s#   1C3 B>C3 3	D<D=DD)F)(django.contrib.auth.decoratorsr   django.shortcutsr   django.views.decorators.csrfr   django.httpr   r   	django.dbr   django.utils.timezoner	   r
   services.foxReportServicer   services.connexionsServicer   pyodbcpandaspdcsvapp.Models.fox_products_modelsr   r    r9   r&   r.   r4   r;   r?   rC   rE   rT   rX   r^   r`   r2   r   r   r   ra   r   <module>r      s    9 # 4 2 ! %  ; 5   
 :0s 0&s &TMd|c |@  c   Hjs jZT93 T9v&P5C 5D 5n8x
s 
t 
)# )ra   