a
    0i)                     @   s"  d dl m Z  d dlmZ d dlZd dlZd dlmZmZmZm	Z	 d dl
mZ edZG dd dZG d	d
 d
Zdd Zeee  dddZeee dddZeee dddZeee eedddZeeeedddZeeeedddZeeeeddd Zeeed!d"d#ZdS )$    datetime)DecimalN)OptionalListDictUnionget_connectionZrecepcion_cotizacion_sqlc                   @   sd   e Zd ZdZd	eeeeeeeeeeeeeeeeeeeeeeeeeeeedddZedddZ	dS )
OpportunityAndCostingDTOuI   DTO para la información unificada de Oportunidad y Encabezado de Costeo.N
costing_idcrm_opportunity_numbercrm_contact_namecrm_contact_typecrm_assigned_salespersoncrm_contact_adresscrm_contact_coloniacrm_contact_citycrm_contact_numbercrm_contact_countrycrm_contact_legal_identifiercrm_contact_zipcrm_contact_statecrm_contact_email	case_costsale_price_listsale_price_mindiscount_max_percentrun_time_numberrun_time_typetechnical_terms_and_conditionsFinancePercentTaxCodeCurrencyCodeQ_TaxRate_FrontESQ_TaxRate_FrontENQ_Currency_FrontESQ_Currency_FrontENc                 C   s   || _ || _|| _|| _|| _|| _|| _|| _|	| _|
| _	|| _
|| _|| _|| _|| _|| _|| _|| _|| _|| _|f| _|| _|f| _|f| _|f| _|f| _|f| _|| _d S )N	CostingIDCRM_OpportunityNumberCRM_ContactNameCRM_ContactTypeCRM_AssignedSalespersonCRM_ContactAdressCRM_ContactColoniaCRM_ContactCityCRM_ContactNumberCRM_ContactCountryCRM_ContactLegalIdentifierCRM_ContactZipCRM_ContactStateCRM_ContactEmailCaseCostSalePriceListSalePriceMinDiscountMaxPercentRunTimeNumberRunTimeTypeTechnicalTermsAndConditionsr"   r#   r$   r%   r&   r'   r(   )selfr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(    r@   A/var/www/html/src/Consultas_SQL/Ventas/Cotiz/RecepcionCotizSQL.py__init__   s8    z!OpportunityAndCostingDTO.__init__returnc                 C   s   | j | j| j| j| j| j| j| j| j| j	| j
| j| j| j| jt| jt| jt| j| j| j| jd | jdurxt| jnd| jd | jd | jd | jd | jd | jdS )z2Convierte el objeto a un diccionario serializable.r   Nr)   )r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   r8   floatr9   r:   r;   r<   r=   r>   r"   r#   r$   r%   r&   r'   r(   )r?   r@   r@   rA   to_dict>   s:    z OpportunityAndCostingDTO.to_dict)
NNNNNNNNNN)
__name__
__module____qualname____doc__strrE   intrB   dictrF   r@   r@   r@   rA   r      s2           -r   c                   @   s   e Zd ZdZeeee dddZee	e
eeeef f  dddZee	e
eef  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edddZdS )RecepcionCotizSQLuY   Clase de servicio para manejar la lógica de recepción de datos de costeo y oportunidad.)costing_numrD   c                 C   sv  d}z4t  }| $}|||  | }W d   n1 sD0    Y  |rt|d |d |d |d |d |d |d	 |d
 |d |d |d |d |d |d |d |d |d |d |d |d |d |d |d |d |d |d |d |d dW  d   W S W d   W dS 1 s.0    Y  W n6 typ } ztd |  W Y d}~dS d}~0 0 dS )!z{
        Consulta unificada que obtiene los datos de Q_CostingHead y Q_OpportunityCRM
        en una sola llamada.
        aO  
            SELECT TOP 1
                Q_CostingHead.CostingID,
                Q_OpportunityCRM.CRM_OpportunityNumber,
                Q_OpportunityCRM.CRM_ContactName,
                Q_OpportunityCRM.CRM_ContactType,
                Q_OpportunityCRM.CRM_AssignedSalesperson,
                Q_OpportunityCRM.CRM_ContactAdress,
                Q_OpportunityCRM.CRM_ContactColonia,
                Q_OpportunityCRM.CRM_ContactCity,
                Q_OpportunityCRM.CRM_ContactNumber,
                Q_OpportunityCRM.CRM_ContactCountry,
                Q_OpportunityCRM.CRM_ContactLegalIdentifier,
                Q_OpportunityCRM.CRM_ContactZip,
                Q_OpportunityCRM.CRM_ContactState,
                Q_OpportunityCRM.CRM_ContactEmail,
                Q_CostingHead.CaseCost,
                Q_CostingHead.SalePriceList,
                Q_CostingHead.SalePriceMin,
                Q_CostingHead.DiscountMaxPercent,
                Q_CostingHead.RunTimeNumber,
                Q_CostingHead.RunTimeType,
                Q_CostingHead.TechnicalTermsAndConditions,
                Q_CostingHead.FinancePercent,
                Q_CostingHead.TaxCode,
                Q_CostingHead.CurrencyCode,
                Q_TaxRate.FrontES as Q_TaxRate_FrontES,
                Q_TaxRate.FrontEN as Q_TaxRate_FrontEN,
                Q_Currency.FrontES as Q_Currency_FrontES,
                Q_Currency.FrontEN as Q_Currency_FrontEN
            FROM
                Q_CostingHead
            INNER JOIN
                Q_OpportunityCRM ON Q_CostingHead.CRM_OpportunityID = Q_OpportunityCRM.CRM_OpportunityID
            LEFT JOIN
                Q_TaxRate ON Q_TaxRate.TaxCode = Q_CostingHead.TaxCode 
                        AND Q_TaxRate.CurrencyCode = Q_CostingHead.CurrencyCode 
                        AND Q_TaxRate.Active >= 1
            LEFT JOIN
                Q_Currency ON Q_Currency.CurrencyCode = Q_CostingHead.CurrencyCode
                        AND Q_Currency.Active >= 1
            WHERE
                Q_CostingHead.CostingNum = ?
            ORDER BY
                Q_CostingHead.Version DESC;
        Nr                           	   
                                                      r   z#Error al obtener datos unificados: )r
   cursorexecutefetchoner   	Exceptionprint)rO   queryconnrk   rower@   r@   rA   get_unified_data_by_costing_numc   s6    .

&(z1RecepcionCotizSQL.get_unified_data_by_costing_numrC   c               
   C   s   d} g }zt  |}| T}||  | }|D ],}||d |d t|d |d d q2W d   n1 st0    Y  W d   n1 s0    Y  |W S  ty } ztd|  g W  Y d}~S d}~0 0 dS )	ui   
        Consulta la tabla Q_TaxRate para obtener el código y la descripción de los impuestos.
        zTSELECT TaxCode, FrontES, TaxAmount, CurrencyCode    FROM Q_TaxRate WHERE Active = 1;r   rP   rQ   rR   )r#   FrontES	TaxAmountr$   Nz Error al obtener los impuestos: r
   rk   rl   fetchallappendrE   rn   ro   )rp   taxesrq   rk   rowsrr   rs   r@   r@   rA   	get_taxes   s"    

FzRecepcionCotizSQL.get_taxesc               
   C   s   d} g }zt  r}| J}||  | }|D ]"}||d |d |d d q2W d   n1 sj0    Y  W d   n1 s0    Y  |W S  ty } ztd|  g W  Y d}~S d}~0 0 dS )uw   
        Consulta la tabla Q_Currency para obtener el código, el nombre y el símbolo de las monedas activas.
        zJSELECT CurrencyCode, FrontES, CurrSymbol FROM Q_Currency WHERE Active = 1;r   rP   rQ   )r$   ru   
CurrSymbolNzError al obtener las monedas: )r
   rk   rl   rx   ry   rn   ro   )rp   
currenciesrq   rk   r{   rr   rs   r@   r@   rA   get_currencies   s    

Bz RecepcionCotizSQL.get_currenciesc                 C   s   d}g }zt  }| p}|||  | }|D ]F}||d |d |d t|d |d t|d t|d d	 q4W d
   n1 s0    Y  W d
   n1 s0    Y  |W S  t y } ztd|  g W  Y d
}~S d
}~0 0 d
S )uN   
        Consulta Q_CostingDetail para obtener las líneas de costeo.
        a  
            SELECT 
                CostingLine,
                PartNum,
                PartDescription,
                Qty,
                UOMCode,
                UnitPrice,
                Amount
            FROM Q_CostingDetail
            WHERE CostingID = (
                SELECT TOP 1 CostingID 
                FROM Q_CostingHead 
                WHERE CostingNum = ? 
                ORDER BY Version DESC
            )
            ORDER BY CostingLine ASC;
        r   rP   rQ   rR   rS   rT   rU   )CostingLinePartNumPartDescriptionQtyUOMCode	UnitPriceAmountNz%Error al obtener detalles de costeo: rw   )rO   rp   detailsrq   rk   r{   rr   rs   r@   r@   rA   get_costing_details   s*    



F	z%RecepcionCotizSQL.get_costing_details)datarD   c              7   C   s  zddl m} ddlm} | d}d}t }| T}||| | }|d }d}	||	|f | }
|
r|
d nd}|std|  W d	   W d	   W d	S |d }|d
 r|d
 nd}|d| | }|d sd
n
|d d
 }| d}|}|r>|d| | }|r>|d r>|d }td| d|  td|d  d td|d  d td|d  d |d r|d 	 nd	}|d r|d 	 nd	}|d r|d 	 nd	}|||| d| d| d| d|d |d |d |d |d |d  |d! |d" |d# |p@d$|pHd%|pPd&| d'd| d(d| d)d||| d*d||| d+d
| d,g t
| d,g | d-| d.| jd/d0| d1d2| d3d4| d5| d6| d7| d8| d9| d:| d;| d<| d=| d>| d?| d@| dA| dBdC3}tdD|dE   |dHi |}|W  d	   W  d	   W S 1 s0    Y  W d	   n1 s0    Y  W nF ty } z,tdG|  dd	l}|  W Y d	}~d	S d	}~0 0 d	S )Iuw   
        Genera HTML de vista previa de la cotización SIN guardar en BD.
        Retorna el HTML renderizado.
        r   )render_templater   r*   u  
                SELECT TOP 1
                    Q_CostingHead.CostingNum,
                    Q_CostingHead.Version,
                    
                    -- Cliente
                    Q_OpportunityCRM.CRM_ContactName,
                    Q_OpportunityCRM.CRM_ContactType,
                    Q_OpportunityCRM.CRM_OpportunityNumber,
                    Q_OpportunityCRM.CRM_ContactEmail,
                    Q_OpportunityCRM.CRM_ContactNumber,
                    Q_OpportunityCRM.CRM_ContactCity,
                    Q_OpportunityCRM.CRM_ContactState,
                    Q_OpportunityCRM.CRM_ContactCountry,
                    Q_OpportunityCRM.CRM_ContactAdress,
                    
                    -- ✅ El vendedor es el UserID de la oportunidad
                    TRIM(CONCAT(
                        ISNULL(Seller.FirstName, ''), ' ',
                        ISNULL(Seller.MiddleName, ''), ' ', 
                        ISNULL(Seller.LastName, ''), ' ',
                        ISNULL(Seller.SecondLastName, '')
                    )) AS VendedorNombre,
                    ISNULL(Seller.Email, '') AS VendedorEmail,
                    ISNULL(Seller.ContactPhone, '') AS VendedorTelefono,
                    Q_CostingHead.TaxCode

                    
                FROM Q_CostingHead
                
                INNER JOIN Q_OpportunityCRM 
                    ON Q_CostingHead.CRM_OpportunityID = Q_OpportunityCRM.CRM_OpportunityID
                
                -- ✅ JOIN directo: El vendedor es Q_OpportunityCRM.UserID
                LEFT JOIN Profiles AS Seller
                    ON Q_OpportunityCRM.UserID = Seller.UserID
                
                WHERE Q_CostingHead.CostingID = ?
            r]   z
                        select TaxPercent
                        from Q_TaxRate
                        where TaxCode = ?
                    u   ❌ No se encontró CostingID: NrP   m
                        SELECT MAX(Version) FROM Q_QuotationHead WHERE QuotationNum = ?
                    r$   zk
                            SELECT FrontES FROM Q_Currency WHERE CurrencyCode = ?
                        u   💱 Moneda: z -> u   🔍 DEBUG - VendedorNombre: 'rZ   'u   🔍 DEBUG - VendedorEmail: 'r[   u    🔍 DEBUG - VendedorTelefono: 'r\   r8   r<   r=   r>   rQ   rR   rS   rT   rU   rV   rW   rX   rY   zNo asignadozventas@igsa.comzN/A	SalePriceDiscountPercentr   TotalAmountOvercostFactorQuotationLinesz%d/%m/%Yz%H:%MIGSAz"Integradora de Servicios Avanzadosproyecto_nombrezPor definirproyecto_requerimientosZNingunocaseSelectedcase1AnticipoPercentcase1FiniquitoPercentcase2AnticipoPercentcase2FrequencyTypecase2Periodicidadcase2Cantidadcase2CantidadPeriodicidadcase3FrequencyTypecase3Periodicidadcase3Cantidadcase3CantidadPeriodicidadcase4DiasFinanciamientocase5PlazoCreditoFlag)3quotation_numversionr   caso_costeor<   r=   r>   cliente_nombretipo_contactooportunidad_crmcliente_emailcliente_telefonocliente_ciudadcliente_estadocliente_paisZcliente_direccionvendedor_asignadovendedor_emailvendedor_telefonoprecio_listadescuento_porcentajeprecio_ofertaimpuesto_codigoZimpuedesto_porcentajeprecio_totalmonedamoneda_codigofactor_sobrecostolineastotal_lineasfecha_actualZhora_actualu   año_actualempresaempresa_completar   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   u    ✅ Vendedor final en template: r   *Emails/Ventas/Cotiz/PreviewCotizacion.htmlu#   ❌ Error al generar vista previa: )r   )flaskr   r   getr
   rk   rl   rm   ro   striplennowstrftimeyearrn   	traceback	print_exc)r   r   r   r   rp   rq   rk   rr   ZtaxCodequery2tax_rowZtax_percentr   Zcurrent_versionversion_resultZnext_versioncurrency_codeZcurrency_nameZcurrency_resultvendedor_nombrer   r   template_datahtmlrs   r   r@   r@   rA   generate_quotation_preview	  s    
+









TXz,RecepcionCotizSQL.generate_quotation_previewc                 C   s  zNt  0}| }| d}|d| | }|sddddW  d   W  d   W S |d }|d| | }|d sd	n
|d d	 }| d
| }td|  d}	||	||| d| d| dd| dd	| d| d| d| d| d| d| d| d| d| d| d| d| d| d| d| d | d!| d"f | d#g }
d$}|
D ]T}||||d%|d&|d'|d(|d)|d*|d+|df	 q|  td,| d- W d   n1 s0    Y  d.d/||d0W  d   W S 1 sD0    Y  W nB ty } z(td1|  dt|dW  Y d}~S d}~0 0 dS )2u   
        Crea una nueva cotización en Q_QuotationHead y Q_QuotationDetail.
        Envía notificación por correo al departamento de ingeniería.
        r*   zf
                        SELECT CostingNum FROM Q_CostingHead WHERE CostingID = ?
                    FzCostingID no encontrado)successerrorNr   r   rP   -u   Creando cotización: a  
                        INSERT INTO Q_QuotationHead (
                            QuotationNum,
                            Version,
                            CaseCost,
                            SalePrice,
                            DiscountPercent,
                            OvercostFactor,
                            Amount,
                            TaxCode,
                            TotalAmount,
                            CurrencyCode,
                            Active,
                            caseSelected,
                            case1AnticipoPercent,
                            case1FiniquitoPercent,
                            case2AnticipoPercent,
                            case2FrequencyType,
                            case2Periodicidad,
                            case2Cantidad,
                            case2CantidadPeriodicidad,
                            case3FrequencyType,
                            case3Periodicidad,
                            case3Cantidad,
                            case3CantidadPeriodicidad,
                            case4DiasFinanciamiento,
                            case5PlazoCreditoFlag
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    r8   r   r   r   r   r#   r   r$   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   a  
                        INSERT INTO Q_QuotationDetail (
                            QuotationID,
                            QuotationLine,
                            CostingLineID,
                            PartNum,
                            PartDescription,
                            Qty,
                            UOMCode,
                            UnitPrice,
                            Amount
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                    ZQuotationLineZCostingLineIDr   r   r   r   r      Cotización z creada exitosamente en BDTu   Cotización creada exitosamente)r   messageZQuotationIDVersionu   Error al crear cotización: )	r
   rk   r   rl   rm   ro   commitrn   rK   )r   rq   rk   r   Zcosting_resultr   r   r   quotation_idinsert_head_queryZquotation_linesinsert_detail_queryliners   r@   r@   rA   create_quotation  s    

(


0,z"RecepcionCotizSQL.create_quotation)r   rD   c              
   C   s  zddl m} | }| }|d|  | }|r|d rL|d  nd|d rb|d  nd|d rx|d  nddW  d   W  d   W S W d   n1 s0    Y  W d   n1 s0    Y  W n4 ty } ztd|  W Y d}~n
d}~0 0 ddddS )	u!   Obtiene información del vendedorr   r	   a  
                        SELECT TOP 1
                            TRIM(CONCAT(
                                ISNULL(Seller.FirstName, ''), ' ',
                                ISNULL(Seller.MiddleName, ''), ' ', 
                                ISNULL(Seller.LastName, ''), ' ',
                                ISNULL(Seller.SecondLastName, '')
                            )) AS VendedorNombre,
                            ISNULL(Seller.Email, '') AS VendedorEmail,
                            ISNULL(Seller.ContactPhone, '') AS VendedorTelefono
                            
                        FROM Q_CostingHead
                        INNER JOIN Q_OpportunityCRM 
                            ON Q_CostingHead.CRM_OpportunityID = Q_OpportunityCRM.CRM_OpportunityID
                        LEFT JOIN Profiles AS Seller
                            ON Q_OpportunityCRM.UserID = Seller.UserID
                        WHERE Q_CostingHead.CostingID = ?
                    NrP   rQ   )nombreemailtelefonou+   ⚠️ Error al obtener info del vendedor: )Consultas_SQL.conexionr
   rk   rl   rm   r   rn   ro   )r   r
   rq   rk   rr   rs   r@   r@   rA   get_seller_infoe  s     
d$z!RecepcionCotizSQL.get_seller_infoN)rG   rH   rI   rJ   staticmethodrL   r   r   rt   r   r   rK   r   rE   r|   r   r   rM   r   r   r   r@   r@   r@   rA   rN   `   s"   O$- S 	rN   c                 C   s>   t | trt| S t | tr$|  S tdt| j ddS )zO
    Convierte objetos Decimal y datetime a formatos compatibles con JSON.
    zObject of type z is not JSON serializableN)
isinstancer   rE   r   	isoformat	TypeErrortyperG   )objr@   r@   rA   json_serializer  s
    

r   rC   c            	   
      s  d} t  }|std dS zpz| }||  dd |jD  | }|sttd W W |  t	d dS  fdd|D }|D ]\}|
 D ]N\}}t|tr| ||< qt|trt|||< qt|trt|||< qq|W W |  t	d S  tjy> } z"td	t|   W Y d}~nDd}~0  tyx } z"td
t|   W Y d}~n
d}~0 0 W |  t	d n|  t	d 0 dS )u  
    Obtiene todos los registros activos del catálogo Q_TermsType y los devuelve
    como una lista de diccionarios serializables a JSON.

    Returns:
        List[Dict]: Lista de registros de términos y condiciones.
        None: Si no se encuentran registros o ocurre un error.
    a  
        SELECT
            TermsTypeID,
            FrontES,
            FrontEN,
            Active,
            createdAt,
            CreatedBy,
            UpdatedAt,
            UpdatedBy
        FROM Q_TermsType
        WHERE Active = 1
        ORDER BY TermsTypeID ASC;
    u9   ❌ No se pudo establecer conexión con la base de datos.Nc                 S   s   g | ]}|d  qS r   r@   ).0colr@   r@   rA   
<listcomp>      z&get_TermsType_JSON.<locals>.<listcomp>u:   ⚠️ No se encontraron registros activos en Q_TermsType.u*   🔒 Conexión a la base de datos cerrada.c                    s   g | ]}t t |qS r@   rM   zipr   rr   columnsr@   rA   r     r   u    Error SQL al obtener términos: z(Error inesperado en get_TermsType_JSON: )r
   loggerr   rk   rl   descriptionrx   warningcloseinfoitemsr   r   r   r   rE   boolpyodbcErrorrK   rn   )	rp   rq   rk   r{   resultsrkeyvaluers   r@   r   rA   get_TermsType_JSON  sN    	






	
r   )cotizacion_idrD   c              
      s^  | st d dS d}d}t }|s,tdzzr| }|||  dd |jD }| }|st d|   W W |r|	  t 
d	 dS tt||}|||  d
d |jD  | } fdd|D }	||	d}
d|
d v r|
d d }t|tr| |
d d< |
d  D ]&\}}t|tr"t||
d |< q"|
d D ]2}| D ]"\}}t|tr^t|||< q^qR|
W W |r|	  t 
d	 S  tjy } z(t d|  dt|   W Y d}~nDd}~0  ty" } z"t dt|   W Y d}~n
d}~0 0 W |rZ|	  t 
d	 n|rX|	  t 
d	 0 dS )u  
    Busca el encabezado y el detalle de una cotización usando su ID primario,
    y los devuelve en un diccionario con tipos de datos serializables a JSON.

    Args:
        cotizacion_id (str): El ID primario de la cotización (ej. "1001-1").

    Returns:
        Un diccionario con la estructura de QuotationHead y QuotationDetail,
        con tipos de datos serializables a JSON, o None si no se encuentra.
    u(   Se proporcionó un cotizacion_id vacío.Na  
        SELECT
            QuotationID, QuotationDate, QuotationNum, Version, SalePrice,
            DiscountPercent, Amount, TaxCode, TotalAmount, CurrencyCode,
            DeliveryTime, TermsAndConditions
        FROM Q_QuotationHead
        WHERE QuotationID = ?;
    a  
        SELECT
            QuotationLineID, QuotationID, QuotationLine, CostingLineID,
            PartNum, PartDescription, Qty, UOMCode, UnitPrice, Amount
        FROM Q_QuotationDetail
        WHERE QuotationID = ?
        ORDER BY QuotationLine ASC;
    u5   No se pudo establecer conexión con la base de datos.c                 S   s   g | ]}|d  qS r   r@   r   columnr@   r@   rA   r     r   z3get_Quote_JSON_BussinessCentral.<locals>.<listcomp>u6   No se encontró encabezado de cotización para el ID: u%   Conexión a la base de datos cerrada.c                 S   s   g | ]}|d  qS r   r@   r  r@   r@   rA   r     r   c                    s   g | ]}t t |qS r@   r   r   Zcolumns_detailr@   rA   r     r   )QuotationHeadQuotationDetailZQuotationDater  r  u.   Error de base de datos al buscar cotización 'z': z5Error inesperado en get_Quote_JSON_BussinessCentral: )r   r   r
   ConnectionErrorrk   rl   r   rm   r   r   r   rM   r   rx   r   r   r   r   r   rE   r   r   rK   rn   )r   Z
query_headZquery_detailrq   rk   Zcolumns_headZrow_headZquotation_head_dataZrows_detailZquotation_detail_dataZ
final_dataZdate_objr   r   r   rs   r@   r  rA   get_Quote_JSON_BussinessCentral  sp    
		0
	
r  c                    s&  z&|  d\}}| }t| }W n$ tyJ   td|   Y dS 0 d}t }|sbtdzz| | }|	|||f dd |j
D }| }|std|   W d   W W |r|  dS i }g }	|D ]j}
tt||
 |s fd	d
 D }| |d< |	|d< |	 d  d  d  d  d  d d q|W  d   W W |rp|  S 1 sz0    Y  W nx tjy } z"tdt|   W Y d}~nDd}~0  ty } z"tdt|   W Y d}~n
d}~0 0 W |r"|  n|r |  0 dS )u   
    Busca los datos de la oportunidad, las líneas de ingeniería y el estado.
    
    Args:
        cotizacion_id (str): ID de la oportunidad (Ej: 123456-1)
    
    Returns:
        Dict: Datos de la oportunidad y las líneas o None.
    r   u$   Formato de cotizacion_id inválido: Nu  
        SELECT
            T1.CRM_OpportunityNumber,
            T1.Version,
            T1.CRM_ContactName,
            T1.CRM_ContactEmail,
            T1.Status AS StatusVenta,
            -- Asumimos una tabla de estado de ingeniería
            T2.StatusIngenieria, 
            T3.Partnum,
            T3.Description,
            T3.Quantity,
            T3.UnitPriceIngenieria, -- Precio que puso Ingeniería
            T3.DiscountVenta,       -- Descuento que puede modificar Venta
            T3.FinalPrice,
            -- Campos financieros ya calculados (si existen)
            T1.PrecioLista,
            T1.PrecioVentaIVA,
            T1.TiempoEntrega,
            T1.UnidadTiempo,
            T1.TerminosIngenieria -- Términos de Ingeniería
        FROM Q_OpportunityCRM T1
        LEFT JOIN Q_QuotationHead T2 ON T1.CRM_OpportunityID = T2.CRM_OpportunityID 
        LEFT JOIN Q_QuotationLines T3 ON T2.QuotationID = T3.QuotationID 
        WHERE 
            T1.CRM_OpportunityNumber = ? AND T1.Version = ? AND T1.Active = 1
            AND T2.StatusIngenieria IS NOT NULL -- Solo cotizaciones que han pasado por ingeniería
        ORDER BY T3.LineNum ASC
    u4   No se pudo establecer conexión con la base de datosc                 S   s   g | ]}|d  qS r   r@   r  r@   r@   rA   r   }  r   z.buscar_cotizacion_completa.<locals>.<listcomp>u-   No se encontraron datos para Cotización ID: c                    s   i | ]}|d vr| | qS )PartnumDescriptionQuantityUnitPriceIngenieriaDiscountVenta
FinalPricer@   )r   kr   r@   rA   
<dictcomp>  r   z.buscar_cotizacion_completa.<locals>.<dictcomp>r   r   r	  r
  r  r  r  r  r  u.   Error de base de datos al buscar cotización: z0Error inesperado en buscar_cotizacion_completa: )splitr   rL   
ValueErrorr   r   r
   r  rk   rl   r   rx   r   r   rM   r   ry   r   r   rK   rn   )r   Z	op_numberr   rp   rq   rk   r   r{   headerlinesrr   rs   r@   r  rA   buscar_cotizacion_completaA  s^    !
		r  )r   r   user_idrD   c              
   C   s   |sdddS z@ddl m} ||}tdt| d|  d|  dd	dW S  ty } z.td
t|  dt|dW  Y d}~S d}~0 0 dS )z?Actualiza campos editables (ej. Descuento) en Q_QuotationLines.Tu   No hay líneas para actualizarr   r   rP   obtener_nombre_usuariozActualizando u    líneas para z por u   Líneas actualizadasu,   Error al actualizar líneas de cotización: FN)CotizEspSolicitudSQLr  r   r   r   rn   r   rK   )r   r   r  r  	user_namers   r@   r@   rA   actualizar_lineas_cotizacion  s    
 r  )r   financierosr  rD   c           	   
   C   sb  d}t  }|sdddS z2zddlm} ||}| | }|||d |d |d	 || f |  |jd
krdddW  d   W W |r|  S t	d|   ddiW  d   W W |r|  S 1 s0    Y  W nV t
jy: } z:tdt|  dddW  Y d}~W |r0|  S d}~0 0 W |r^|  n|r\|  0 dS )uI   Actualiza la sección financiera en Q_OpportunityCRM (o Q_QuotationHead).a1  
        UPDATE Q_OpportunityCRM
        SET 
            DiscountPorcentaje = ?,
            IncludesIVA = ?,
            PrecioVentaIVA = ?,
            UpdatedBy = ?,
            UpdatedAt = GETDATE()
        WHERE CRM_OpportunityID = ? -- Asumiendo que el ID de la oportunidad es el cotizacion_id
    F   Error de conexión a BDr  rP   r  	descuentoZincluye_ivaZprecio_final_con_ivar   u8   Oportunidad no encontrada para actualización financieraNz$Datos financieros actualizados para r   Tz'Error de BD al actualizar financieros: Error de base de datosr
   r  r  rk   rl   r   rowcountr   r   r   r   r   r   rK   )	r   r  r  rp   rq   r  r  rk   rs   r@   r@   rA   actualizar_calculo_financiero  s<    
r$  )r   tiempo_condicionesr  rD   c           	   
   C   sb  d}t  }|sdddS z2zddlm} ||}| | }|||d |d |d	 || f |  |jd
krdddW  d   W W |r|  S t	d|   ddiW  d   W W |r|  S 1 s0    Y  W nV t
jy: } z:tdt|  dddW  Y d}~W |r0|  S d}~0 0 W |r^|  n|r\|  0 dS )uM   Actualiza el tiempo de entrega y los términos técnicos en Q_OpportunityCRM.u  
        UPDATE Q_OpportunityCRM
        SET 
            TiempoEntrega = ?,
            UnidadTiempo = ?,
            TerminosVenta = ?, -- Nuevo campo para términos de Venta
            UpdatedBy = ?,
            UpdatedAt = GETDATE()
        WHERE CRM_OpportunityID = ?
    Fr  r  rP   r  tiempo_ejecucionZunidad_tiempoZterminos_ventar   u<   Oportunidad no encontrada para actualización de condicionesNz'Tiempo y condiciones actualizados para r   Tz0Error de BD al actualizar tiempo y condiciones: r!  r"  )	r   r%  r  rp   rq   r  r  rk   rs   r@   r@   rA   actualizar_tiempo_y_condiciones  s<    
r'  )r   r  r   rD   c              
   C   sx   z,t | |}td|  d|  d|dW S  tyr } z.tdt|  dt|dW  Y d}~S d}~0 0 dS )	u   
    Marca el estado final de la cotización, genera el PDF y registra el evento.
    
    Nota: La generación de PDF con Puppeteer/Node.js debería llamarse aquí.
    r   z0 marcada como ENVIADA_A_CLIENTE y PDF generado: T)r   pdf_urlu.   Error al finalizar cotización y generar PDF: Fr  N)generar_pdf_final_cotizacionr   r   rn   r   rK   )r   r  r   r(  rs   r@   r@   rA   finalizar_cotizacion_y_enviar  s    
r*  )r   r   rD   c                 C   s   d|  dS )uI   Simula la generación de PDF final (debería llamar a Node.js/Puppeteer).z*https://sycelephant.com/static/pdfs/cotiz/z
_final.pdfr@   )r   r   r@   r@   rA   r)  1  s    r)  )r   decimalr   r   loggingtypingr   r   r   r   r   r
   	getLoggerr   r   rN   r   r   rK   r  r  rL   r  r$  r'  r*  r)  r@   r@   r@   rA   <module>   s*   
R    2@fg.-