a
    0i.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   ]/var/www/html/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y } z,| t | dt || jdW  Y d	}~S d	}~0 0 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                    sn   d}t  }|stdzH| }||||f dd |jD   fdd| D W |  S |  0 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>       z.BOMExploder._get_materials.<locals>.<listcomp>c                    s   g | ]}t t |qS r   dictziprH   rowcolumnsr   r   rJ      rK   NConexionBD_ERPr   cursorexecutedescriptionfetchallcloser
   r   r   query
connectionrU   r   rQ   r   r9   r   s    %zBOMExploder._get_materialsc                    sn   d}t  }|stdzH| }||||f dd |jD   fdd| D W |  S |  0 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      rK   z/BOMExploder._get_operations.<locals>.<listcomp>c                    s   g | ]}t t |qS r   rL   rO   rQ   r   r   rJ      rK   NrS   rZ   r   rQ   r   r6      s    zBOMExploder._get_operations)r   r   c                 C   sZ   d}t  }|sdS z8| }|||f | }|r>|d ndW |  S |  0 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   )rT   rU   rV   fetchonerY   )r
   r   r[   r\   rU   resultr   r   r   r=      s    z BOMExploder._get_latest_revision)r   r   c                 C   sV   d}t  }|stdz0| }||| j||f |  W |  n
|  0 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   rU   rV   r	   commitrY   rZ   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 |  n
|  0 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   rU   rV   r	   rC   ra   rY   )	r
   rB   rb   rc   r$   rd   r[   r\   rU   r   r   r   r<      s$    
zBOMExploder._insert_material)	operationrb   rc   r$   rd   c           	      C   s   d}t  }|stdzn| }||| j|||||d |d |d d|d |d |d	 |d
 td td tf |  W |  n
|  0 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_   re   r&   ZOpCodeNZOpDescrf   EstProdHoursrh   r,   r-   )	r`   r   rU   rV   r	   r!   r@   ra   rY   )	r
   ri   rb   rc   r$   rd   r[   r\   rU   r   r   r   r8     s"    	
zBOMExploder._insert_operation)processing_time_msc                 C   s`   d}t  }|sdS z>| }||| j| j| jt|| jf |  W |  n
|  0 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`   rU   rV   r	   intra   rY   )r
   rk   r[   r\   rU   r   r   r   r   ,  s    


z!BOMExploder._update_process_stats)error_messagec                 C   sP   d}t  }|sdS z.| }|||| jf |  W |  n
|  0 dS )u   Marca el proceso como erróneozx
        UPDATE BOM_Process_Control 
        SET Status = 'Error', ErrorMessage = ?
        WHERE ProcessID = ?
        N)r`   rU   rV   r	   ra   rY   )r
   rm   r[   r\   rU   r   r   r   r   F  s    
zBOMExploder._mark_process_errorc                 C   s   d}t  }|stdzZ| }|| | }|r^dd |jD }tt||W |  S tdW |  n
|  0 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  rK   z.BOMExploder._get_pricingMO.<locals>.<listcomp>z"No se encontraron datos de CostLbrN)	r`   r   rU   rV   r]   rW   rM   rN   rY   )r
   r[   r\   rU   rP   rR   r   r   r   r   [  s    

zBOMExploder._get_pricingMO)	pricingMOri   c                 C   sJ   t |dpd}t |dp d}t |dp2d}|| ||  }|S )Nrj   r   r,   r-   )floatget)r
   rn   ri   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z| }|||d |d |d |d |d |d f | }|rdd |jD }tt||W |  S tdW |  n
|  0 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   rg   r5   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ     rK   z5BOMExploder._get_pricingMaterials.<locals>.<listcomp>z#No se encontraron datos de PartTranN)	rT   r   rU   rV   r]   rW   rM   rN   rY   )r
   rB   r[   r\   rU   rP   rR   r   r   r   r:     s     0
z!BOMExploder._get_pricingMaterialsN)__name__
__module____qualname__r   r   rM   r#   rl   r   listr9   r6   r=   r   r<   r8   ro   r   r   r   r7   r:   r   r   r   r   r      s   $43% r   )new_ext_costr	   r   c              
   C   s  d}zzt  }|s.dddW W |r,|  S | }d}||| ||f |j}|  |dkrdd| d| dW W |r|  S d	d
|dW W |r|  S  t y } z:|r|  ddt| dW  Y d}~W |r|  S d}~0 0 W |r|  n|r|  0 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: )	r`   rY   rU   rV   rowcountra   r   rollbackr   )rv   r	   r   r\   rU   update_queryZ	row_countr"   r   r   r   get_status_manual  sT    ,	
r|   )r	   r   c                 C   s  d}d}d}d}t  }|s$dddS zzH| }||| | 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 |r|  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 |rr|  S  ty } z4|  dt|| dW  Y d }~W |r|  S d }~0 0 W |r|  n|r|  0 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
    Frw   rx   r   r               )material_costmaterial_componentsoperation_costoperation_componentsrq   )partNummaterialCostmaterialComponentsoperationCostoperationComponents	totalCostz+No se encontraron componentes para calcular        rq   )r   ZcalculatedCostr   
componentsr(   r   r   )r   ZpreviousCostZnewCostZstatusChanger   r   c                 s   s   | ]}|d  V  qdS )rq   Nr   rH   vr   r   r   	<genexpr>  rK   z(calculate_total_costs.<locals>.<genexpr>c                 s   s   | ]}|d  V  qdS )r   Nr   r   r   r   r   r     rK   T)r   updatedMaterialstotalCalculatedCostZtotalMaterialCostZtotalOperationCostZverificationDataZcostBreakdown	processId)r   r   r   )r`   rU   rV   rX   ro   rl   appendrY   rp   ra   roundsumvaluesr   rz   r   )r	   Zcalculate_costs_queryZupdate_fabricated_materialsZget_fabricated_materialsZverification_queryZdatabase_connectionZdatabase_cursorZ	cost_dataZcost_breakdownrP   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   sH  d}z,zt  }|std| }||||| ||| f | }|sbdddW W |r`|  S |d d durt|d d nd}t|d	kr|d	 d durt|d	 d nd}d
|| ||dW W |r|  S  ty  }	 z0ddt|	 dW  Y d}	~	W |r|  S d}	~	0 0 W |rD|  n|rB|  0 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 registrosrx   r   Nr   r   T)r   r   r   r   zError en base de datos: )	r`   r   rU   rV   rX   rY   ro   lenr   )
r	   r   r   r[   r\   rU   resultsr   r   r"   r   r   r   get_total_cost_for_parent  s@    $0
 
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 |  0 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     rK   z get_bom_data.<locals>.<listcomp>c                    s   g | ]}t t |qS r   rL   rO   )materials_columnsr   r   rJ     rK   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ     rK   c                    s   g | ]}t t |qS r   rL   rO   )operations_columnsr   r   rJ     rK   c                 S   s   g | ]}|d  qS rF   r   rG   r   r   r   rJ      rK   N)rA   r>   control)	r`   rU   rV   rW   rX   r]   rM   rN   rY   )r	   Zquery_materialsZquery_operationsZquery_controlr\   rU   rA   r>   Zcontrol_columnsZcontrol_datar   r   )r   r   r   get_bom_data  s2    r   )r[   r   c                    sv   d}t  }|sg S zT| }d|  d}||||f dd |jD   fdd| D W |  S |  0 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  rK   z search_parts.<locals>.<listcomp>c                    s   g | ]}t t |qS r   rL   rO   rQ   r   r   rJ   G  rK   N)rT   rU   rV   rW   rX   rY   )r[   Zsearch_queryr\   rU   Zsearch_termr   rQ   r   search_parts-  s    r   )r   r   configr   r   Consultas_SQL.conexionr   r   r   r`   rT   r   ro   r   r|   rM   r   r   r   ru   r   r   r   r   r   <module>   s"      &7 BA<