U
    i.z                     @   s   d dl Z d dlmZ d dlmZmZ d dlmZmZmZ erBeZ	neZ	eZ
G dd dZeeeddd	Zeed
ddZeeeedddZeed
ddZeedddZdS )    N)datetime)
ProductivoENVIRONMENT)get_connectionbdproductivoget_connectionget_connectionERPc                   @   s   e Zd Zdd ZeeedddZeeeedddZeee	dd	d
Z
eee	dddZeedddZeedddZeeeeedddZeeeeedddZedddZedddZdd  Zeed!d"d#Zed$d%d&Zd'S )(BOMExploderc                 C   s
   d | _ d S )N)
process_id)self r   C:\Users\victor.barrera\Documents\proyectos\elepV3\Elep\src\Consultas_SQL\SupYCtrol\GerenteSyC\UtilityMaterials\GSYCManufacBOMIngSQL.py__init__   s    zBOMExploder.__init__)part_numrevisionreturnc              
   C   s   t t | _t }zT| || |  }| |||dd t | 	 d }| 
| d| jddW S  tk
r } z(| t | dt || jd W Y S d	}~X Y nX d	S )
uO   
        Explota una BOM multinivel y retorna información del proceso
        r      i  TzBOM explodida exitosamente)successr	   messageF)r   errorr	   N)struuiduuid4r	   r   now_init_process_control_get_pricingMO_explode_recursivetotal_seconds_update_process_stats	Exception_mark_process_error)r
   r   r   
start_time	PricingMOZprocessing_timeer   r   r   explode_bom   s$    
zBOMExploder.explode_bom)r   r   levelorder_startc                 C   s   |}|  ||}|D ].}| t|}| ||||||t |d7 }q| ||}	|	D ]}
|
d dkrp| |
}ndddddddddddd}td| | |
||||| |d7 }|
d d	krT| |
d
 }|rT| 	t|
d
 ||d |}qT|S )u5   Explota un nivel específico de la BOM recursivamenter   TypePNr   	CALCULADO)TranNumTranDateMtlUnitCostLbrUnitCostBurUnitCostSubUnitCostMtlBurUnitCostExtCostPOLineStatusz$$$$MPartNum)
_get_operations_get_totalcost_operationr!   _insert_operation_get_materials_get_pricingMaterialsprint_insert_material_get_latest_revisionr   )r
   r   r   r$   r%   Zcurrent_order
operationsopCost	materialsmaterialCostMatZchild_revisionr   r   r   r   >   sH    

zBOMExploder._explode_recursivec                    sh   d}t  }|stdzB| }||||f dd |jD   fdd| D W S |  X dS )-Obtiene materiales de la BOM desde Epicor ERPa  
        SELECT 
            Erp.PartMtl.MtlSeq AS Seq,
            Erp.Part.TypeCode AS [Type],
            CASE Erp.Part.TypeCode 
                WHEN 'M' THEN 'Fabricado'
                WHEN 'P' THEN 'Material' 
                ELSE Erp.Part.TypeCode 
            END AS TipoDesc,
            Erp.PartMtl.MtlPartNum AS PartNum,
            
            (
                SELECT TOP 1 Erp.PartRev.RevisionNum 
                FROM Erp.PartRev 
                WHERE Erp.PartRev.Company = Erp.PartMtl.Company 
                    AND Erp.PartRev.PartNum = Erp.PartMtl.MtlPartNum 
                ORDER BY Erp.PartRev.EffectiveDate DESC 
            ) AS Rev,

            Erp.Part.PartDescription,
            LEFT(Erp.Part.PartDescription, 35) AS ShortDescription,
            Erp.PartMtl.QtyPer,
            Erp.PartMtl.UOMCode

        FROM 
            Erp.PartMtl 
        LEFT JOIN 
            Erp.Part ON Erp.PartMtl.Company = Erp.Part.Company 
            AND Erp.PartMtl.MtlPartNum = Erp.Part.PartNum 
        WHERE 
            Erp.PartMtl.Company = 'IGSA' 
            AND Erp.PartMtl.PartNum = ?
            AND Erp.PartMtl.RevisionNum = ?
        ORDER BY 
            Erp.PartMtl.MtlSeq
           Error de conexión a ERPc                 S   s   g | ]}|d  qS r   r   .0columnr   r   r   
<listcomp>   s     z.BOMExploder._get_materials.<locals>.<listcomp>c                    s   g | ]}t t |qS r   dictziprH   rowcolumnsr   r   rJ      s     NConexionBD_ERPr   closecursorexecutedescriptionfetchallr
   r   r   query
connectionrU   r   rP   r   r9   r   s    %zBOMExploder._get_materialsc                    sh   d}t  }|stdzB| }||||f dd |jD   fdd| D W S |  X dS )z.Obtiene operaciones de la BOM desde Epicor ERPa-  
        SELECT 
            Erp.PartOpr.OprSeq AS Seq,
            Erp.PartOpr.SubContract AS [Type],
            CASE Erp.PartOpr.SubContract 
                WHEN 0 THEN 'Mano de Obra' 
                WHEN 1 THEN 'Subcontrato' 
            END AS TipoDesc,
            Erp.PartOpr.OpCode,
            Erp.OpMaster.OpDesc,
            LEFT(Erp.OpMaster.OpDesc, 35) AS ShortDescription,
            Erp.PartOpr.EstProdHours,
            '' AS UOMCode                         

        FROM Erp.PartOpr 
        LEFT JOIN Erp.OpMaster ON Erp.PartOpr.Company = Erp.OpMaster.Company 
            AND Erp.PartOpr.OpCode = Erp.OpMaster.OpCode 
        WHERE Erp.PartOpr.Company = 'IGSA' 
            AND Erp.PartOpr.PartNum = ?
            AND Erp.PartOpr.RevisionNum = ?
        ORDER BY Erp.PartOpr.OprSeq
        rE   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ      s     z/BOMExploder._get_operations.<locals>.<listcomp>c                    s   g | ]}t t |qS r   rK   rN   rP   r   r   rJ      s     NrR   rY   r   rP   r   r6      s    zBOMExploder._get_operations)r   r   c                 C   sT   d}t  }|sdS z2| }|||f | }|r>|d ndW S |  X dS )u/   Obtiene la revisión más reciente de una partez
        SELECT TOP 1 RevisionNum 
        FROM Erp.PartRev 
        WHERE Company = 'IGSA' AND PartNum = ?
        ORDER BY EffectiveDate DESC
        Nr   )rS   rT   rU   rV   fetchone)r
   r   rZ   r[   rU   resultr   r   r   r=      s    z BOMExploder._get_latest_revision)r   r   c                 C   sN   d}t  }|stdz(| }||| j||f |  W 5 |  X dS )z4Inicializa el registro de control del proceso en VPSz
        INSERT INTO BOM_Process_Control 
        (ProcessID, PartNumFather, RevisionNumFather, Status, ProcessDate)
        VALUES (?, ?, ?, 'Processing', GETDATE())
           Error de conexión a VPSN)ConexionBD_VPSr   rT   rU   rV   r	   commitrY   r   r   r   r      s    z!BOMExploder._init_process_control)rB   part_father
rev_fatherr$   orderc           	      C   s   d}t  }|stdz| }||| j|||||d |d |d |d |d |d |d	 |d
 td td td td td td td td td td td f |  W 5 |  X dS )z3Inserta un material en la tabla BOM_Materials (VPS)a  
        INSERT INTO BOM_Materials 
        (ProcessID, PartNumFather, RevisionNumFather, [Order], [Level], Seq, [Type], 
        PartNum, Rev, PartDescription, ShortDescription, QtyPer, UOMCode)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        r^   Seqr&   r5   ZRevZPartDescriptionShortDescriptionQtyPerUOMCoder)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   N)r_   r   rT   rU   rV   r	   rC   r`   )	r
   rB   ra   rb   r$   rc   rZ   r[   rU   r   r   r   r<      sD                    zBOMExploder._insert_material)	operationra   rb   r$   rc   c           	      C   s   d}t  }|stdzf| }||| j|||||d |d |d d|d |d |d	 |d
 td td tf |  W 5 |  X dS )u7   Inserta una operación en la tabla BOM_Operations (VPS)a,  
        INSERT INTO BOM_Operations 
        (ProcessID, PartNumFather, RevisionNumFather, [Order], [Level], Seq, [Type], 
        OpCode, Rev, OpDesc, ShortDescription, EstProdHours, UOMCode, LbrUnitCost, BurUnitCost, ExtCost)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        r^   rd   r&   ZOpCodeNZOpDescre   EstProdHoursrg   r,   r-   )	r_   r   rT   rU   rV   r	   r!   r@   r`   )	r
   rh   ra   rb   r$   rc   rZ   r[   rU   r   r   r   r8     s4             	zBOMExploder._insert_operation)processing_time_msc                 C   sX   d}t  }|sdS z6| }||| j| j| jt|| jf |  W 5 |  X dS )u#   Actualiza estadísticas del procesoa  
        UPDATE BOM_Process_Control 
        SET Status = 'Completed',
            TotalMaterials = (SELECT COUNT(*) FROM BOM_Materials WHERE ProcessID = ?),
            TotalOperations = (SELECT COUNT(*) FROM BOM_Operations WHERE ProcessID = ?),
            MaxLevel = (SELECT ISNULL(MAX([Level]), 0) FROM BOM_Materials WHERE ProcessID = ?),
            ProcessingTime_MS = ?
        WHERE ProcessID = ?
        N)r_   rT   rU   rV   r	   intr`   )r
   rj   rZ   r[   rU   r   r   r   r   ,  s    
   z!BOMExploder._update_process_stats)error_messagec                 C   sH   d}t  }|sdS z&| }|||| jf |  W 5 |  X dS )u   Marca el proceso como erróneozx
        UPDATE BOM_Process_Control 
        SET Status = 'Error', ErrorMessage = ?
        WHERE ProcessID = ?
        N)r_   rT   rU   rV   r	   r`   )r
   rl   rZ   r[   rU   r   r   r   r   F  s    zBOMExploder._mark_process_errorc                 C   sr   d}t  }|stdzL| }|| | }|rXdd |jD }tt||W S tdW 5 |  X dS )z Obtiene desde VPS z`
        SELECT 
            LbrUnitCost, 
            BurUnitCost
        FROM CostLbr
        r^   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ   m  s     z.BOMExploder._get_pricingMO.<locals>.<listcomp>z"No se encontraron datos de CostLbrN)	r_   r   rT   rU   rV   r\   rW   rL   rM   )r
   rZ   r[   rU   rO   rQ   r   r   r   r   [  s    
zBOMExploder._get_pricingMO)	pricingMOrh   c                 C   sJ   t |dpd}t |dp d}t |dp2d}|| ||  }|S )Nri   r   r,   r-   )floatget)r
   rm   rh   Z	est_hoursZlbr_unit_costZbur_unit_cost
total_costr   r   r   r7   u  s
    z$BOMExploder._get_totalcost_operation)rB   c              
   C   s   d}t  }|stdzr| }|||d |d |d |d |d |d f | }|r~dd |jD }tt||W S tdW 5 |  X dS )	rD   uj  
        SELECT
            TranNum,
            TranDate,
            MtlUnitCost,
            LbrUnitCost,
            BurUnitCost,
            SubUnitCost,
            MtlBurUnitCost,
            PONum AS PO,
            POLine AS Line,
            ([MtlUnitCost] * ? + [LbrUnitCost] * ? + [BurUnitCost] * ? + [SubUnitCost] * ? + [MtlBurUnitCost] * ?) AS ExtCost,
            CASE            
                -- Si está en los últimos 90 días 
                WHEN TranDate >= DATEADD(day, -90, GETDATE()) THEN 'COSTEADOS'
                
                -- Si está entre 90 y 180 días 
                WHEN TranDate >= DATEADD(day, -180, GETDATE())
                    AND TranDate < DATEADD(day, -90, GETDATE()) THEN 'REVISION' 

                -- Si no cumple las condiciones anteriores (sin TranNum/TranDate o más de 180 días)
                ELSE 'INSERTAR'
            END AS Status                
                    
        FROM 
            Erp.PartTran 
        WHERE 
            Erp.PartTran.Company = 'IGSA' 
            AND Erp.PartTran.PartNum = ?
        ORDER BY Erp.PartTran.TranDate DESC 
        rE   rf   r5   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ     s     z5BOMExploder._get_pricingMaterials.<locals>.<listcomp>z#No se encontraron datos de PartTranN)	rS   r   rT   rU   rV   r\   rW   rL   rM   )r
   rB   rZ   r[   rU   rO   rQ   r   r   r   r:     s     0z!BOMExploder._get_pricingMaterialsN)__name__
__module____qualname__r   r   rL   r#   rk   r   listr9   r6   r=   r   r<   r8   rn   r   r   r   r7   r:   r   r   r   r   r      s   $43% r   )new_ext_costr	   r   c              
   C   s   d}zz|t }|s"dddW W S | }d}||| ||f |j}|  |dkrrdd| d| dW W bS d	d
|dW W PS  tk
r } z,|r|  ddt| d W Y W S d}~X Y nX W 5 |r|   X dS )zDActualiza el ExtCost y cambia el Status a MANUAL en la base de datosNF%   Error de conexión a la base de datosr   r   z
        UPDATE BOM_Materials
        SET ExtCost = ?,
            Status = 'MANUAL'
        WHERE ProcessID = ?
          AND PartNum = ?
        r   u(   No se encontró el registro. ProcessID: z, PartNum: TzCosto actualizado exitosamente)r   r   Zupdated_rowszError en la base de datos: )	rT   r_   rU   rV   rowcountr`   r   rollbackr   )ru   r	   r   r[   rU   update_queryZ	row_countr"   r   r   r   get_status_manual  s8    	$r{   )r	   r   c                 C   s  d}d}d}d}t  }|s$dddS zz4| }||| | f i }g }| D ]}	|	d }
t|	d	 t|	d
 t|	d t|	d t|	d d||
< ||
t|	d	 t|	d
 t|	d t|	d t|	d d qT|sdddW W S ||| f g }d}| D ]R}	|	d }
||
||
i ddt|	d	 t|	d
 d |t|	d	 7 }q||| f | }g }|D ]x\}
}}||
i dd}|||| |
f ||
|rt|nd|d||
i dd||
i ddd q|	  t
tdd | D d}t
tdd | D d}d||t
|d|||| dW W JS  tk
r } z$|  dt|| d W Y W S d }~X Y nX W 5 |r|  X d S )Nu  
    -- Materiales comprados agrupados por padre con precisión decimal
    WITH MaterialCosts AS (
        SELECT 
            PartNumFather AS PartNum,
            CAST(SUM(COALESCE(CAST(ExtCost AS DECIMAL(18,8)), 0)) AS DECIMAL(18,8)) AS MaterialCost,
            COUNT(*) AS MaterialComponents
        FROM BOM_Materials
        WHERE ProcessID = ? 
          AND Type = 'P'
        GROUP BY PartNumFather
    ),
    
    -- Costos de operaciones agrupados por padre con precisión decimal
    OperationCosts AS (
        SELECT 
            PartNumFather AS PartNum,
            CAST(SUM(COALESCE(CAST(ExtCost AS DECIMAL(18,8)), 0)) AS DECIMAL(18,8)) AS OperationCost,
            COUNT(*) AS OperationComponents
        FROM BOM_Operations
        WHERE ProcessID = ?
        GROUP BY PartNumFather
    ),
    
    -- Costos combinados con verificación de NULLs
    CombinedCosts AS (
        SELECT
            m.PartNum,
            CAST(COALESCE(m.MaterialCost, 0) AS DECIMAL(18,8)) AS MaterialCost,
            m.MaterialComponents,
            CAST(COALESCE(o.OperationCost, 0) AS DECIMAL(18,8)) AS OperationCost,
            o.OperationComponents,
            CAST(COALESCE(m.MaterialCost, 0) + COALESCE(o.OperationCost, 0) AS DECIMAL(18,8)) AS TotalCost
        FROM MaterialCosts m
        LEFT JOIN OperationCosts o ON m.PartNum = o.PartNum
        
        UNION
        
        SELECT
            o.PartNum,
            CAST(0 AS DECIMAL(18,8)) AS MaterialCost,
            0 AS MaterialComponents,
            CAST(o.OperationCost AS DECIMAL(18,8)) AS OperationCost,
            o.OperationComponents,
            CAST(o.OperationCost AS DECIMAL(18,8)) AS TotalCost
        FROM OperationCosts o
        WHERE NOT EXISTS (SELECT 1 FROM MaterialCosts m WHERE m.PartNum = o.PartNum)
    )
    
    SELECT 
        PartNum,
        MaterialCost,
        MaterialComponents,
        OperationCost,
        OperationComponents,
        TotalCost
    FROM CombinedCosts
    WHERE PartNum IS NOT NULL
    ORDER BY PartNum
    z
    UPDATE BOM_Materials
    SET ExtCost = CAST(? AS DECIMAL(18,8)),
        Status = 'CALCULADO'
    WHERE ProcessID = ?
      AND PartNum = ?
      AND Type = 'M'  -- FABRICADO
    z
    SELECT PartNum, 
           CAST(ExtCost AS DECIMAL(18,8)) AS ExtCost, 
           Status
    FROM BOM_Materials
    WHERE ProcessID = ? 
      AND Type = 'M'  -- FABRICADO
    z
    SELECT 
        PartNumFather, 
        SUM(COALESCE(CAST(ExtCost AS DECIMAL(18,8)), 0)) AS TotalMaterialCost,
        COUNT(*) AS Components
    FROM BOM_Materials 
    WHERE ProcessID = ? 
      AND Type = 'P'
    GROUP BY PartNumFather
    Frv   rw   r   r               )material_costmaterial_componentsoperation_costoperation_componentsrp   )partNummaterialCostmaterialComponentsoperationCostoperationComponents	totalCostz+No se encontraron componentes para calcular        rp   )r   ZcalculatedCostr   
componentsr(   r   r   )r   ZpreviousCostZnewCostZstatusChanger   r   c                 s   s   | ]}|d  V  qdS )rp   Nr   rH   vr   r   r   	<genexpr>  s     z(calculate_total_costs.<locals>.<genexpr>c                 s   s   | ]}|d  V  qdS )r   Nr   r   r   r   r   r     s     T)r   updatedMaterialstotalCalculatedCostZtotalMaterialCostZtotalOperationCostZverificationDataZcostBreakdown	processId)r   r   r   )r_   rT   rU   rV   rX   rn   rk   appendro   r`   roundsumvaluesr   ry   r   )r	   Zcalculate_costs_queryZupdate_fabricated_materialsZget_fabricated_materialsZverification_queryZdatabase_connectionZdatabase_cursorZ	cost_dataZcost_breakdownrO   r   Zverification_dataZtotal_material_costZfabricated_materialsZupdated_materialsZcurrent_costZcurrent_statusZnew_costZtotal_calculated_costZtotal_operation_costr   r   r   r   calculate_total_costs  s    =	













	

	$r   )r	   r   r   r   c           
   
   C   s  d}z zt }|std| }||||| ||| f | }|sXdddW W S |d d dk	rxt|d d nd}t|d	kr|d	 d dk	rt|d	 d nd}d
|| ||dW W FS  tk
r }	 z ddt|	 d W Y W S d}	~	X Y nX W 5 |r|   X dS )u-   Función con mejor manejo de errores para SQLa  
    SELECT
        SUM(COALESCE(BOM_Materials.ExtCost, 0)) AS TotalCostMaterial
    FROM
        BOM_Materials
    WHERE
        BOM_Materials.PartNumFather = ?
        AND BOM_Materials.RevisionNumFather = ?
        AND BOM_Materials.ProcessID = ?

    UNION ALL

    SELECT
        SUM(COALESCE(BOM_Operations.ExtCost, 0)) AS TotalCostOperation
    FROM 
        BOM_Operations
    WHERE
        BOM_Operations.PartNumFather = ?
        AND BOM_Operations.RevisionNumFather = ?
        AND BOM_Operations.ProcessID = ?
    z&No se pudo conectar a la base de datosFzNo se encontraron registrosrw   r   Nr   r   T)r   r   r   r   zError en base de datos: )	rT   r_   r   rU   rV   rX   rn   lenr   )
r	   r   r   rZ   r[   rU   resultsr   r   r"   r   r   r   get_total_cost_for_parent  s*    $02r   c                    s   d}d}d}t  }|sddiS z| }||| f dd |jD   fdd| D }||| f d	d |jD fd
d| D }||| f dd |jD }| }	|	rtt||	nd}
|||
dW S |  X dS )z,Obtiene datos completos de una BOM explodidaaJ  
    SELECT 
        [Level], [Order], Seq, [Type], PartNum, Rev, 
        PartDescription, ShortDescription, QtyPer, UOMCode, TranNum,
        TranDate, MtlUnitCost, LbrUnitCost, BurUnitCost, 
        SubUnitCost, MtlBurUnitCost, ExtCost, PO, Line, Status
    FROM BOM_Materials 
    WHERE ProcessID = ?
    ORDER BY [Order]
    a:  
    SELECT 
        [Level], [Order], Seq, [Type], OpCode, 
        OpDesc, ShortDescription, EstProdHours, UOMCode, TranNum,
        TranDate, MtlUnitCost, LbrUnitCost, BurUnitCost, 
        SubUnitCost, MtlBurUnitCost, ExtCost, PO, Line
    FROM BOM_Operations 
    WHERE ProcessID = ?
    ORDER BY [Order]
    z?
    SELECT * FROM BOM_Process_Control WHERE ProcessID = ?
    r   r^   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ     s     z get_bom_data.<locals>.<listcomp>c                    s   g | ]}t t |qS r   rK   rN   )materials_columnsr   r   rJ     s     c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ     s     c                    s   g | ]}t t |qS r   rK   rN   )operations_columnsr   r   rJ     s     c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ      s     N)rA   r>   control)	r_   rT   rU   rV   rW   rX   r\   rL   rM   )r	   Zquery_materialsZquery_operationsZquery_controlr[   rU   rA   r>   Zcontrol_columnsZcontrol_datar   r   )r   r   r   get_bom_data  s.    
r   )rZ   r   c                    sp   d}t  }|sg S zN| }d|  d}||||f dd |jD   fdd| D W S |  X dS )u+   Busca partes en el ERP por nombre o códigoa  
    SELECT TOP 10 
        p.PartNum, 
        p.PartDescription,
        pr.RevisionNum,
        p.TypeCode
    FROM Erp.Part p
    LEFT JOIN Erp.PartRev pr ON p.Company = pr.Company AND p.PartNum = pr.PartNum
    WHERE p.Company = 'IGSA' 
        AND p.TypeCode = 'M'  -- Solo manufacturados
        AND (p.PartNum LIKE ? OR p.PartDescription LIKE ?)
    ORDER BY p.PartNum, pr.EffectiveDate DESC
    %c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ   F  s     z search_parts.<locals>.<listcomp>c                    s   g | ]}t t |qS r   rK   rN   rP   r   r   rJ   G  s     N)rS   rT   rU   rV   rW   rX   )rZ   Zsearch_queryr[   rU   Zsearch_termr   rP   r   search_parts-  s    r   )r   r   configr   r   Consultas_SQL.conexionr   r   r   r_   rS   r   rn   r   r{   rL   r   r   r   rt   r   r   r   r   r   <module>   s"      &7 BA<