
    tz0i                        S r SSKJr  SSKrSSKrSSKJrJrJrJr  SSK	J
r
JrJrJr  \R                  " S5      rSSKJrJr  SSKJr  SS	KJr  SS
KJr  SSKJr  SSK	Jr  S rS rS rS$S\4S jjrS\\\\4      4S jr S\\\\\\!4   4      4S jr"S r#S r$S r%S r&S r'S%S\4S jjr(S\)S\4S jr*S\\\      4S jr+S\!S\\   4S  jr,S!\!S\\   4S" jr-S!\S\\   4S# jr.g)&u   
Archivo: CotizCreatedIngSQL.py
Ruta: src\Consultas_SQL\Operaciones\Ingenieria\Cotiz\CotizCreatedIngSQL.py
Descripción: Módulo para crear cotizaciones del area de ingenierìa
Autor: Equipo de Desarrollo IGSA
Fecha: 2025
    )datetimeN)DictListOptionalUnion)	Blueprintrequestjsonifysessioncotizaciones_especiales_sql)
ProductivoENVIRONMENT)get_connection) upload_document_to_existing_docs)crear_docs_head)enviar_correo_universal)r
   c                    SnSn [        5        nUR                  5       nUR                  X45        UR                  5       nU(       d  [	        SU  35         SSS5        gUR
                   Vs/ s H  ofS   PM	     nn[        [        Xu5      5      nUR                  X 45        UR                  5       n	U	(       a  U	S   OSUS'   [        UR                  S5      [        5      (       a  US   R                  S5      US'   UsSSS5        $ s  snf ! , (       d  f       g= f! [         a  n
[	        S	U  S
U
 35         Sn
A
gSn
A
ff = f)u   
🔹 Obtiene y une la información necesaria para el envío de correo
   basada en el TaskID (tabla Q_SpQ_QuotationTasks).
🔹 Devuelve un diccionario listo para usar como `template_data`.
aH  
        SELECT 
            Q_OpportunityCRM.CRM_OpportunityNumber,
            Q_CostingHead.CostingNum,
            Q_QuotationType.FrontEN,
            Q_QuotationType.FrontES,
            Q_CostingHead.CostingDate
        FROM 
            Q_SpQ_QuotationTasks
        INNER JOIN 
            Q_SpQ_FormsHead
            ON Q_SpQ_QuotationTasks.FormID = Q_SpQ_FormsHead.FormID
        INNER JOIN 
            Q_QuotationType
            ON Q_SpQ_FormsHead.QuotationTypeID = Q_QuotationType.QuotationTypeID
        INNER JOIN 
            Q_CostingHead
            ON Q_CostingHead.CostingID = Q_SpQ_QuotationTasks.CostingID
        INNER JOIN
            Q_OpportunityCRM
        ON
            Q_SpQ_QuotationTasks.CRM_OpportunityID = Q_OpportunityCRM.CRM_OpportunityID
        WHERE 
            Q_SpQ_QuotationTasks.TaskID = ?;
    a`  
        SELECT 
            Users.Email
        FROM Users
        INNER JOIN 
            Q_CostingHead
            ON Q_CostingHead.SellerUserID = Users.UserID
        INNER JOIN 
            Q_SpQ_QuotationTasks
            ON Q_SpQ_QuotationTasks.CostingID = Q_CostingHead.CostingID
        WHERE 
            Q_SpQ_QuotationTasks.TaskID = ?;
    u0   ⚠️ No se encontró información para TaskID Nr   EmailCostingDatez%d/%m/%Y %H:%Mu   ❌ Error en data_for_email(): )r   cursorexecutefetchoneprintdescriptiondictzip
isinstancegetr   strftime	Exception)task_idquery1query2connr   row1colcolumns1result1row2es              |C:\Users\victor.barrera\Documents\proyectos\elepV3\Elep\src\Consultas_SQL\Operaciones\Ingenieria\Cotiz\CotizCreatedIngSQL.pydata_for_emailr-      s5   F4F[[]F NN6:.??$DH	RS  +1*<*<=*<3A*<H=3x./G NN6:.??$D*.tAwDGG
 '++m4h??)0)?)H)HIY)Z&1  > 4  ,WIS<=sT   
D# A	DD# "D0D>BD	D# D
D D#  D# #
E-EEc                    SnSn [        5       nUR                  5       nSnUR                  X@U45        UR                  5       nUUb  UR	                  5         Ub  UR	                  5         $ $ ! [
         aA  n[        SU 35        / s SnAUb  UR	                  5         Ub  UR	                  5         $ $ SnAff = f! Ub  UR	                  5         Ub  UR	                  5         f f = f)zj
Consulta los documentos asociados a una oportunidad y form (task)
y regresa las filas crudas del cursor.
Na  
            SELECT
                Q_SpQ_FormsHead.CRM_OpportunityID,
                Q_SpQ_FormsHead.FormID,
                Q_SpQ_DocsDetail.Title,
                Q_SpQ_DocsDetail.Ruta,
                Q_SpQ_DocsDetail.Type
            FROM
                Q_SpQ_FormsHead
            LEFT JOIN Q_SpQ_DocsHead
                ON Q_SpQ_FormsHead.DocsID = Q_SpQ_DocsHead.DocsID
            LEFT JOIN Q_SpQ_DocsDetail
                ON Q_SpQ_DocsHead.DocsID = Q_SpQ_DocsDetail.DocsID
            WHERE 
                Q_SpQ_FormsHead.CRM_OpportunityID = ?
                AND Q_SpQ_FormsHead.FormID = ?
                AND Q_SpQ_FormsHead.DocsID IS NOT NULL
        zError al consultar documentos: )r   r   r   fetchallcloser!   r   )oportunity_idr"   
connectionr   queryrowsr+   s          r,   &consultar_docs_por_oportunidad_y_tarear5   l   s    
 JF$#%
""$& 	ug67  LLN! "  /s34	 LLN! " LLN! "s0   A A. .
B98B4B9B< 4B99B< <)C%c                 n   SU;  a  SUS'   U c  UR                  S5      n U (       a  U(       ax  SnS n [        5       nU   UR                  5       nUR                  X0U45        UR	                  5         US==   SU  3-  ss'   UsS S S 5        U(       a  UR                  5         $ $ U$ ! , (       d  f       OJ= f! [         a:  nSUS'   S[        U5       3US'   Us S nAU(       a  UR                  5         $ $ S nAff = f U(       a  UR                  5         U$ ! U(       a  UR                  5         f f = f)	Nmessage docs_idY
        UPDATE Q_CostingHead
        SET DocsID = ?
        WHERE CostingID = ?
        z" | Costing actualizado con DocsID Fsuccessz
Error DB: )r   r   r   r   commitr0   r!   str)DocsID	CostingID	resultador3   r%   r   r+   s          r,   uploadFileAuxiliarrA      s-   	!!	) ~y) )
 	!#Duy&9:)$*LVH(UU$   

     	#(Ii %/Ax#8Ii 

 	  

 

 sS   B9 AB(	B9 (
B62B9 5D 6B9 9
C=C8C=D 8C==D D4destinatarioTestc                    [        U 5      n[        R                  SU 35        U(       d  [        SU  35        SSU  3S.$  SnUS:w  a  UnOUS   n[	        SS	S
U/0US9nUR                  S5      (       a  [        SUS    35        U$ [        SUR                  S5       35        U$ ! [         a)  n[        SU  SU 35        S[        U5      S.s SnA$ SnAff = f)ue   
🔹 Usa los datos de data_for_email(task_id)
🔹 Envía el correo con el template correspondiente
zDatos para correo: u1   ❌ No se pudieron obtener los datos para TaskID Fz$No se encontraron datos para TaskID )r;   mensajer8   r   z0Emails/Ingenieria/Cotiz/CotizCreatedIngMail.htmlu/   ✅ Costeo creado correctamente - IGSA ElephantTO)template_pathasuntodestinatarios_adicionalestemplate_datar;   u"   ✅ Correo enviado exitosamente a u   ⚠️ Error al enviar correo: rD   u'   ❌ Error al enviar correo para TaskID : N)r-   loggerinfor   r   r   r!   r=   )r"   rB   datadestinatarior@   r+   s         r,   enviarCorreoCostingCreadorO      s   
 '"D
KK%dV,-A'KL /ST[S\-]^^5r!+L=L+LD|o' 
	 ==##6tG}oFG  3IMM)4L3MNO 57y1#FG SV445s%   AB) 
B) )
C3CCCreturnc            	         Sn / n [        5        nUR                  5        nUR                  U 5        UR                  5       nU H!  nUR	                  US   US   US   S.5        M#     SSS5        SSS5        U$ ! , (       d  f       N= f! , (       d  f       U$ = f! [
         a  n[        SU 35        / s SnA$ SnAff = f)ug   
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         )CurrencyCodeFrontES
CurrSymbolNzError al obtener las monedas: )r   r   r   r/   appendr!   r   )r3   
currenciesr%   r   r4   rowr+   s          r,   get_currenciesrZ      s     YEJ&u%(C%%),QCFRUVWRXY[         .qc23	sR   
B BA	A<*B2	B <
B
	B
BB B 
C)B>8C>Cc                    Sn/ n [        5        nUR                  5        nUR                  X45        UR                  5       nU H.  nUR	                  US   US   [        US   5      US   S.5        M0     SSS5        SSS5        U$ ! , (       d  f       N= f! , (       d  f       U$ = f! [         a  n[        SU 35        / s SnA$ SnAff = f)	uY   
Consulta la tabla Q_TaxRate para obtener el código y la descripción de los impuestos.
zgSELECT TaxCode, FrontES, TaxAmount, CurrencyCode FROM Q_TaxRate WHERE Active >= 1 AND CurrencyCode = ?;r   rR   rS      )TaxCoderU   	TaxAmountrT   Nz Error al obtener los impuestos: )r   r   r   r/   rW   floatr!   r   )rT   r3   taxesr%   r   r4   rY   r+   s           r,   	get_taxesra      s     z	!T[[]fNN5/:!??,D#QCFY^FZ$58V&= >  $ # " L #] "! L 	4QC89I	sR   
B- BAB
8B 	B- 

B	B
B*%B- *B- -
C7CCCc                 L   SnS n [        5       nU   UR                  5       nUR                  X45        UR                   Vs/ s H  oDS   PM	     nnUR	                  5       nU(       aQ  [        [        XV5      5      n[        R                  SU  S35        UsS S S 5        U(       a   UR                  5         $ $ [        R                  SU  S35         S S S 5        U(       a   UR                  5         g g s  snf !    $ = f!    g = f! , (       d  f       OG= f! [        R                   a-  nSU  SU 3n	[        R                  U	5        [        U	5      eS nAff = f U(       a   UR                  5         g !    g = fg ! U(       a   UR                  5         f !    f = ff = f)	Nzs
        SELECT TOP 1 CRM_OpportunityID, QuotationTypeID
        FROM Q_SpQ_FormsHead
        WHERE FormID = ?
    r   zFormulario z encontrado correctamente.u   No se encontró FormID .z&Error SQL en obtener_datos_formulario(r   r   r   r   r   r   r   r   rK   rL   r0   warningpyodbcErrorerrorr!   )
form_idr3   r%   r   r'   columnsrY   resultr+   msgs
             r,   obtener_datos_formulariorm     s   E D[[]FNN5*-)/););<);#1v);G<//#Cc'/0k'2LMN T( 

  !8	CD ( 

 # =(1 T << 6wis1#FSn ( 

	 4

	    D 1D
C7AD
	D )C<;D
D %D 7D
<D D

DD F D E/(EEF (E9 9E=F#
FF#FF#c                 L   SnS n [        5       nU   UR                  5       nUR                  X45        UR                   Vs/ s H  oDS   PM	     nnUR	                  5       nU(       aQ  [        [        XV5      5      n[        R                  SU  S35        UsS S S 5        U(       a   UR                  5         $ $ [        R                  SU  S35         S S S 5        U(       a   UR                  5         g g s  snf !    $ = f!    g = f! , (       d  f       OG= f! [        R                   a-  nSU  SU 3n	[        R                  U	5        [        U	5      eS nAff = f U(       a   UR                  5         g !    g = fg ! U(       a   UR                  5         f !    f = ff = f)	Na%  
        SELECT TOP 1
            UserRoles.RoleID,
            Roles.CompanyID,
            Roles.DivisionID,
            Roles.DepartamentID,
            Profiles.CompanyID AS ProfileCompanyID,
            Profiles.DivisionID AS ProfileDivisionID,
            Profiles.DepartamentID AS ProfileDepartamentID
        FROM Users
        INNER JOIN Profiles ON Users.UserID = Profiles.UserID
        INNER JOIN UserRoles ON Users.UserID = UserRoles.UserID
        INNER JOIN Roles ON UserRoles.RoleID = Roles.RoleID
        WHERE Users.UserID = ?
    r   zContexto de usuario z recuperado correctamente.%   No se encontró contexto para UserID rc   z&Error SQL en obtener_contexto_usuario(r   rd   )
user_idr3   r%   r   r'   rj   rY   rk   r+   rl   s
             r,   obtener_contexto_usuariorr   8  s   E  D[[]FNN5*-)/););<);#1v);G<//#Cc'/027);UVW T( 

  !FwiqQR ( 

 # =(1 T << 6wis1#FSn ( 

	 4

	 rn   c                      [        5       n U R                  5       nSnUR                  U5        UR                  5       nU(       a  UR                  OS UR                  5         U R                  5         $ ! [         aK  n[        R                  S[        U5       35         SnAWR                  5         W R                  5         gSnAff = f! WR                  5         W R                  5         f = f)u8   Genera el siguiente número de costing (IDENTITY 1000,1)zf
        SELECT ISNULL(MAX(CostingNum), 999) + 1 AS NextCostingNum
        FROM Q_CostingHead
        i  zError al generar CostingNum: N)
r   r   r   r   NextCostingNumr0   r!   rK   rh   r=   )r%   r   r3   rY   r+   s        r,   generar_costing_numru   h  s     	uoo%(s!!d2
 	

  4SVH=>

 	

s*   AA5 5
C
?!C C C

C "C/c                    SnSn U R                  S5      nU R                  S5      nU R                  S5      n[        R                  SU R                  S5       35        U(       a  U(       dT  [        SS05      S	4U(       a  UR	                  5         U(       a'   UR	                  5         [        R                  S
5        $ $ [        R                  SU SU 35        [        U5      nU(       dW  [        SSU 305      S4U(       a  UR	                  5         U(       a'   UR	                  5         [        R                  S
5        $ $ [        U5      nU(       dW  [        SSU 305      S4U(       a  UR	                  5         U(       a'   UR	                  5         [        R                  S
5        $ $ [        5       nUR                  5       nU R                  SS5      nUR                  S5        UR                  5       S   n	U	S-   n
U
 SU 3n[        R                  SU
 SU	 S35        Sn[        S5        UR                  U/ U
PUPUR                  S5      PUPUR                  S5      PUPUR                  S5      PUR                  S5      PUR                  S5      PU R                  S5      PU R                  S 5      PU R                  S!5      PU R                  S"5      PU R                  S#5      PU R                  S$5      PU R                  S%5      PU R                  S&5      PU R                  S'5      PU R                  S(5      PU R                  S)5      PU R                  S*5      PU R                  S+5      PU R                  S,5      PU R                  S-5      PUR                  S.5      PU R                  S/5      PU R                  S05      PU R                  S15      PU R                  S25      PU R                  S35      PU R                  S45      PU R                  S5S65      PU R                  S75      PU R                  S85      P75        [        R                  S9U
 35        [        R                  S:U R                  S5       S;35        U R                  S</ 5      nSnU R                  S5      nUS=:X  a  SnOiUS>;   ac  SU R                  S!S5      S?-  -   SU R                  S#S5      S?-  -   -  SU R                  S%S5      S?-  -   -  SU R                  S'S5      S?-  -   -  n[        R                  S@WSA 35        U(       a  SBnU H  nUR                  UUUR                  SC5      UR                  SD5      UR                  SE5      UR                  SF5      UR                  SG5      UR                  SH5      U-  UR                  SH5      U-  UR                  SF5      -  45        US-  nM     [        R                  SIU SJ35        O[        R                  SK5        U R                  SL5      nSMnUR                  UU5        SNnUR                  UUR                  S.5      45        UR                  5       nUS   nUS:X  aL  SOnUR                  UUR                  S.5      45        [        R                  SPUR                  S.5       SQ35        [        SRU5        SSnUR                  UU45        UR                  5       nU(       a  US   OSn[        R                  STU SUU 35        [        R                  SVU 35        SWnUR                  UUUU45        UR                  5         [        R                  SXU 35        [        S6SYUU
UUR                  S5      UR                  S.5      UU R                  S15      SZ.S[.5      S\4U(       a  UR	                  5         U(       a'   UR	                  5         [        R                  S
5        $ $ !    GN}= f!    $ = f!    GN= f!    $ = f!    GN= f!    $ = f!    N`= f!    $ = f! [        R                   a  nU(       a  UR!                  5         [        R#                  S]U 35        [        S^[%        U5      S_.5      S`4s SnAU(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [        R                  S
5        $ !    $ = f$ SnAf[        R&                   a  nU(       a  UR!                  5         [        R#                  SaU 35        [        Sb[%        U5      S_.5      Sc4s SnAU(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [        R                  S
5        $ !    $ = f$ SnAf[(         a  nU(       a  UR!                  5         [        R#                  SdU 35        [        Se[%        U5      S_.5      Sc4s SnAU(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [        R                  S
5        $ !    $ = f$ SnAff = f! U(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [        R                  S
5        f !    f = ff = f)fu   
Endpoint que crea Q_CostingHead y Q_CostingDetail en una transacción.
Obtiene el último CostingNum, incrementa +1 y lo usa para insertar.
NFormIDUserIDSellerUserIDz
costingId r?   rh   u4   Faltan parámetros: FormID y UserID son obligatorios  u   🔒 Conexión cerradau.   📋 Iniciando creación de Costing - FormID: z
, UserID: u)   No se encontró información para FormID   rp   VersionrR   z7SELECT ISNULL(MAX(CostingNum), 999) FROM Q_CostingHead;r   -u!   🧮 Generando nuevo CostingNum: u    (último fue )a1  
        INSERT INTO Q_CostingHead (
            CostingNum, Version, QuotationTypeID, UserID, RoleID, SellerUserID,
            CompanyID, DivisionID, DepartamentID, CaseCost,
            DirectCost, IndirectPercent, IndirectAmount,
            FinancePercent, FinanceAmount, UtilityPercent, UtilityAmount,
            OperationPercent, OperationAmount, SalePriceMin,
            DiscountMaxPercent, DiscountMaxAmount, SalePriceList, OvercostFactor,
            CRM_OpportunityID, ApprovalProcessID, PreviusDocsID, DocsID,
            TechnicalTermsAndConditions, RunTimeNumber, RunTimeType, Active,
            CurrencyCode, TaxCode
        )
        VALUES (
            ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
            ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
            ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
        )
        z)Inserting into Q_CostingHead with values:QuotationTypeIDRoleID	CompanyID
DivisionIDDepartamentIDCaseCost
DirectCostIndirectPercentIndirectAmountFinancePercentFinanceAmountUtilityPercentUtilityAmountOperationPercentOperationAmountSalePriceMinDiscountMaxPercentDiscountMaxAmountSalePriceListOvercostFactorCRM_OpportunityIDApprovalProcessIDPreviusDocsIDr>   TechnicalTermsAndConditionsRunTimeNumberRunTimeTypeActiveTrT   r]   u9   ✅ Q_CostingHead insertado correctamente con CostingNum=z----case cost: z----Q_CostingDetailA)BCd   zOvercostFactor para detalles: z.6fz
            INSERT INTO Q_CostingDetail (
                CostingID, CostingLine, PartNum, PartDescription,
                Qty, UOMCode, UnitPrice, Amount
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            CostingLinePartNumPartDescriptionQtyUOMCode	UnitPriceu   ✅ z$ items insertados en Q_CostingDetailu4   ⚠️ No hay items en Q_CostingDetail para insertarTaskIDz
        UPDATE Q_SpQ_QuotationTasks
        SET 
            Status = 'COMPLETADO',
            StatusDate = CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'Central America Standard Time' AS DATETIME)
        WHERE TaskID =?;
        z
        SELECT
            COUNT(TaskID) AS NumeroDeTareasPorAsignarNoCompletadas
        FROM
            Q_SpQ_QuotationTasks
        WHERE
            CRM_OpportunityID = ? AND
            Status <> 'COMPLETADO';
        u  
            UPDATE Q_OpportunityCRM
            SET
                Status = 'ATENDIDO',
                UpdatedAt = CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'Central America Standard Time' AS DATETIME) -- Actualiza la fecha de modificación
                --UpdatedBy = 'NOMBRE_USUARIO_QUE_ACTUALIZA' -- Actualiza quién realizó la modificación
            WHERE
                CRM_OpportunityID = ?;
            u   ✅ Oportunidad CRM z actualizada a 'ATENDIDO'zcosting id generado:zh
            select CostingDate
            from Q_CostingHead
            where CostingID = ?;
        u   ✅ CostingDate obtenido: z para CostingID z*task id para actualizar en costing tasks: z
        UPDATE dbo.q_spq_quotationtasks
        SET 
            CostingID = ?,
            CostingDate = ?
        WHERE 
            TaskID = ?
        u)   ✅ Transacción confirmada - CostingID: zCosting creado exitosamente)r?   
CostingNumr|   r   r   ItemsInsertadosr>   )r;   r7   rM      u   ❌ Error de integridad: zError de integridad de datosrh   detailsi  u   ❌ Error de base de datos: zError en base de datos  u   ❌ Error inesperado: zError al crear costing)r   rK   rL   r
   r0   rm   rr   r   r   r   r   r   re   r<   rf   IntegrityErrorrollbackrh   r=   rg   r!   )rM   r%   r   ri   rq   seller_user_idsub1sub2versionlast_numcosting_num
costing_idinsert_head_querydetail_itemsitems_insertadoscasofactorOverCostinsert_detail_queryitemr   query3query4resultQuery4numero_de_tareasquery5query6resultQuery6costing_datequery7db_errorr+   s                                  r,   generar_costing_headSQLr     s
   
 DFP ((8$((8$.1 	j+!6 789gG%[\]_bbx 

45 { 	DWIZX_W`ab
 (0G'PQXPY%Z[\^aaf 

45 i (7G'L^L\%]^_add^ 

45 [ ((9a(
 	PQ??$Q'l#}AgY/
7}NS[R\\]^_
$ 	9:( #+
#+
#+
 HH&'#+
 	#+

 HHX#+
 #+
 HH[!#+
 HH\"#+
 HH_%#+
 HHZ #+
 HH\"#+
 HH&'#+
 HH%&#+
 HH%&#+
 HH_%#+
  HH%&!#+
" HH_%##+
$ HH'(%#+
& HH&''#+
( HH^$)#+
* HH)*+#+
, HH()-#+
. HH_%/#+
0 HH%&1#+
2 HH()3#+
4 HH()5#+
6 HH_%7#+
8 HHX9#+
: HH23;#+
< HH_%=#+
> HH]#?#+
@ HHXt$A#+
B HH^$C#+
D HHYE#+
 #	L 	OP[}]^odhhz&:%;4@A
 xx 126xx
#3;NZdhh0!4s:;dhh/3c9:<dhh/3c9:< dhh115;<>  	4^C4HIJ # %2HH]+HHY'HH./HHUOHHY'XXk*n<XXk*~=O	5 	 !A%  % KK$/00TUVNNQR" 	v( 	v)< =?@('? q F NN6DHH-@$A#CDKK.txx8K/L.MMfgh$Z0
 	v
}-(*6|AD0>Nzl[\@IJ 	v
L&AB
 	?
|LM
 4')"#'88,=#>%)XX.A%B#3((8,
  B 

45  D
 D
 D
 D
1    a0
;<!?CPXMZ[]``` D

45  << [3H:>?!9c(mTUWZZZ D

45   T-aS12!9c!fMNPSSS D

45 T D

45 sN  A6a `%`>a `%`"#a 0`)%`1/Xa `8$%`?```"`&)`.1`58`<?ai&Ac;!i&"i) -b>>c%c33c7;i&Af3i&i) %e66e:%f++f/3i& Ai!i&i) h$$h(3%ii!i&&i) )j?1jj?jj?%j76j?7j;9j?c                 @   [         R                  S5        SnSn [         R                  SU  35        U R                  S5      nU R                  S5      n[         R                  SU 35        [         R                  SU 35        U(       a  U(       dT  [        SS	05      S
4U(       a  UR	                  5         U(       a'   UR	                  5         [         R                  S5        $ $ [         R                  SU 35        [        5       nUR                  5       nSnUR                  XTU45        UR                  S:X  ap  [         R                  SU S35        [        SSU 305      S4U(       a  UR	                  5         U(       a'   UR	                  5         [         R                  S5        $ $ UR                  5         [         R                  SU SU 35        [        SSU 3S.5      S4U(       a  UR	                  5         U(       a'   UR	                  5         [         R                  S5        $ $ !    GN~= f!    $ = f!    N= f!    $ = f!    NP= f!    $ = f! [        R                   a  nU(       a  UR                  5         [         R                  SU 35        [        S[        U5      S.5      S4s SnAU(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [         R                  S5        $ !    $ = f$ SnAf[          a  nU(       a  UR                  5         [         R                  SU 35        [        S[        U5      S.5      S4s SnAU(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [         R                  S5        $ !    $ = f$ SnAff = f! U(       a  UR	                  5         O!    O= fU(       a.   UR	                  5         [         R                  S5        f !    f = ff = f)z6
Endpoint para actualizar el DocsID en Q_CostingHead.
u8   🔄 Iniciando actualización de DocsID en Q_CostingHeadNu%   Datos recibidos para actualización: r?   r>   zcostingId recibido: zdocsId recibido: rh   u7   Faltan parámetros: CostingID y DocsID son obligatoriosrz   u*   🔒 Conexión a la base de datos cerrada.u8   📋 Iniciando actualización de DocsID para CostingID: r:   r   u$   ⚠️ No se encontró el CostingID z para actualizar.u*   No se encontró un registro con CostingID r{   u(   ✅ DocsID actualizado correctamente a 'z' para CostingID Tz.DocsID actualizado correctamente en CostingID )r;   r7      u1   ❌ Error de base de datos al actualizar DocsID: zError en la base de datosr   r   u+   ❌ Error inesperado al actualizar DocsID: u   Ocurrió un error inesperado)rK   rL   r   r
   r0   r   r   r   rowcountre   r<   rf   rg   r   rh   r=   r!   r	   get_json)rM   r%   r   r   r9   update_queryr   r+   s           r,   update_costing_head_docsidSQLr     s    KKJKDFO;D6BC
 XXk*
((8$*:,78'y12G%^_`beev 

HI y 	Nzl[\
 

 	|z%:;
 ??aNNA*M^_`G'QR\Q]%^_`beeB 

HI ? 	>wiGXYcXdef
 G
|T
  , 

HI  D
 D
 D
- << ^H
ST!<XWXZ]]] D

HI   ZB1#FG!?CPQFSTVYYY D

HI Z D

HI s   BI (H1 %H9'BI /I %I.=I 2I
%I1H69H= IIIIO0AL7O8O KK#%L		LOAN?%O&O 1NN%N77N;?OO PO P O$"P/%PPPPr?   c           	         [        US[        U5      5      nUS   n/ nU  HC  n[        UUUR                  SU 3SU 3S9n	U	S   (       d  M,  UR	                  U	S   S   5        ME     [        U5      S	:  U[        U5      US
.$ )u9   
Patrón simplificado para casos con una sola categoría
Q_CostingHeadr9   zArchivo de zVentas/Costeo/)file_objr9   titler   ruta_completar;   rM   download_urlr   )r;   r9   archivos_subidosurls)r   obtener_nombre_usuarior   filenamerW   len)
archivos	modulo_idrq   r?   	categoriadocs_resultr9   urls_generadasarchivor@   s
             r,   upload_archivos_simpler   H  s     "'?<RSZ<[\K)$G N4""%i[1*9+6
	 Y!!)F"3N"CD  ~&*/	     rq   c           	         SnSn [        5       nU(       d  SU  3U(       a   UR                  5         $ $ U   UR                  5       nUR                  X45        UR	                  5       nU(       a`  SR                  US   R                  5       5      nUR                  5       (       a  UOSU  3sSSS5        U(       a   UR                  5         $ $ SU  3sSSS5        U(       a   UR                  5         $ $ !    $ = f!    $ = f!    $ = f! , (       d  f       Oc= f! [         aS  n[        R                  SU  S[        U5       35        SU  3s SnAU(       a   UR                  5         $ !    $ = f$ SnAff = f U(       a   UR                  5         g!    g= fg! U(       a   UR                  5         f !    f = ff = f)z
Obtiene el nombre completo del usuario desde la tabla Profiles

Args:
    user_id (int): ID del usuario

Returns:
    str: Nombre completo del usuario
a  
        SELECT 
            CONCAT(
                COALESCE(Profiles.FirstName, ''), ' ',
                COALESCE(Profiles.LastName, ''), ' ',
                COALESCE(Profiles.SecondLastName, '')
            ) AS FullName
        FROM Profiles
        WHERE Profiles.UserID = ?
            AND Profiles.UserID IN (
                SELECT Users.UserID 
                FROM Users 
                WHERE Users.Status = 'ACTIVO'
            )
    NUsuario_ r   z#Error al obtener nombre de usuario rJ   )r   r0   r   r   r   joinsplitstripr!   rK   re   r=   )rq   r3   r%   r   rY   	full_namer+   s          r,   r   r   g  s   E  DgY'& 

 # [[]FNN5*-//#CHHSV\\^4	$-OO$5$5yXgY;O T" 

  "'+ T" 

 + T  $<WIRAxPQ'## 

	 $ " 

	 4

	 s   D C8D A7D.	D ?C?D	D &D8C<?DD

DD F  D 
E;((E6E;F  E..E26E;;F  F F G)F:9G:F><Gc                     Sn Sn [        5       nU(       d  [        S5      eU   UR                  5       nUR                  U 5        UR                   Vs/ s H  o3S   PM	     nnUR                  5       nU(       a  / nU HW  n[        [        XG5      5      nS H)  n	X;   d  M
  X   (       d  M  X   R                  5       X'   M+     UR                  U5        MY     [        R                  S[        U5       S35        UsSSS5        U(       a   UR                  5         $ $ [        R                  S	5         SSS5        U(       a   UR                  5         ggs  snf ! [         a    [        R                  S5         $ f = f! [         a    [        R                  S5         gf = f! , (       d  f       O= f! [         R"                   a3  n
S
[%        U
5       3n[        R'                  U5        [        U5      eSn
A
f[         a3  n
S[%        U
5       3n[        R'                  U5        [        U5      eSn
A
f[         a3  n
S[%        U
5       3n[        R'                  U5        [        U5      eSn
A
ff = f U(       a7   UR                  5         g! [         a    [        R                  S5         gf = fg! U(       a7   UR                  5         f ! [         a    [        R                  S5         f f = ff = f)uN  
Busca todos los datos de score en la tabla Score de la base de datos

Returns:
    List[Dict]: Lista de diccionarios con los datos de los scores encontrados
    None: Si no se encuentra ningún registro

Raises:
    ConnectionError: Si no se puede establecer conexión con la BD
    Exception: Para otros errores durante la consulta
z)
       SELECT * FROM Score;
       
    N4   No se pudo establecer conexión con la base de datosr   )	CreatedAt	UpdatedAtzSe encontraron z registros de scoreu$   Error al cerrar la conexión a la BDz-No se encontraron registros en la tabla Scorez-Error de base de datos al buscar los scores: u)   Error de conexión al buscar los scores: z'Error inesperado al buscar los scores: )r   ConnectionErrorr   r   r   r/   r   r   	isoformatrW   rK   rL   r   r0   r!   re   rf   rg   r=   rh   )r3   r%   r   columnrj   r4   resultsrY   rk   
date_fieldr+   	error_msgs               r,   buscar_scorer     s   E
 D:G!"XYY[[]FNN5! 06/A/AB/AVay/AGB ??$DC!#g"34F 'A
%/F4F4F171C1M1M1OF. 'A NN6*   oc'l^;NOP3 T` G

 ) NO; ` G

 W C\  GEFG9 GEFGg T> << #CCF8L	Y	"" #?AxH	Y	"" #=c!fXF	Y	""#S ` G

 GEFG 4G

 GEFG s   F8 0F'E"<F'"F'-AF'=	F8 E F'6F8 F F'E?>E?F$#F$'
F51F8 4J8 5F8 8I5.G::I5.H55I5.I00I55J8 J J43J48K8KK8K41K83K44K8r"   c                    SnSn [        5       nU(       d  [        S5      eU   UR                  5       nUR                  X45        UR                   Vs/ s H  oDS   PM	     nnUR                  5       nU(       aw  [        [        XV5      5      n[        R                  SU  S35        SU;   a   US   (       a  US   R                  5       US'   UsSSS5        U(       a   UR                  5         $ $ [        R                  SU  S35         SSS5        U(       a   UR                  5         ggs  snf !    $ = f!    g= f! , (       d  f       O= f! [        R                   a6  nS	U  S
[        U5       3n	[        R!                  U	5        [#        U	5      eSnAf[         a6  nSU  S
[        U5       3n	[        R!                  U	5        [#        U	5      eSnAf["         a6  nSU  S
[        U5       3n	[        R!                  U	5        [#        U	5      eSnAff = f U(       a   UR                  5         g!    g= fg! U(       a   UR                  5         f !    f = ff = f)zM
Busca datos de la oportunidad en la tabla Q_CRM

Args:

Returns:

Raises:


a  
        SELECT TOP 1
            Q_CRM.CRM_OpportunityNumber,
            Q_CRM.CRM_Version,
            Q_CRM.CRM_ContactID,
            Q_CRM.CRM_ContactName,
            Q_CRM.CRM_ContactType,
            Q_CRM.CRM_AssignedSalesperson,
            Q_CRM.CRM_ContactAdress,
            Q_CRM.CRM_ContactColonia,
            Q_CRM.CRM_ContactCity,
            Q_CRM.CRM_ContactNumber,
            Q_CRM.CRM_ContactCountry,
            Q_CRM.CRM_ContactLegalIdentifier,
            Q_CRM.CRM_ContactZip,
            Q_CRM.CRM_ContactState,
            Q_CRM.CRM_ContactEmail,
            Q_CRM.Status,
            Q_CRM.CreatedAt,
            Q_CRM.CreatedBy,
            Q_CRM.Active
        FROM 
            Q_SpQ_QuotationTasks  
            JOIN Q_SpQ_FormsHead   
            ON  Q_SpQ_QuotationTasks.FormID = Q_SpQ_FormsHead.FormID
            JOIN Q_OpportunityCRM  
            ON Q_SpQ_FormsHead.CRM_OpportunityID = Q_OpportunityCRM.CRM_OpportunityID
            JOIN Q_CRM  
            ON Q_OpportunityCRM.CRM_OpportunityNumber = Q_CRM.CRM_OpportunityNumber
        WHERE
            Q_SpQ_QuotationTasks.taskID = ?
            AND Q_CRM.Active = 1
        ORDER BY Q_CRM.CRM_Version DESC;
    Nr   r   Oportunidad  encontrada exitosamenter   " no encontrada en la base de datosz0Error de base de datos al buscar la oportunidad rJ   u+   Error de conexión al buscar la oprtunidad z*Error inesperado al buscar la oportunidad r   r   r   r   r   r   r   r   rK   rL   r   r0   re   rf   rg   r=   rh   r!   )
r"   r3   r%   r   r   rj   rY   rk   r+   r   s
             r,   buscar_tarea_crmr     sK   !EF D9!"XYY[[]FNN5*- 06/A/AB/AVay/AGB //#Cc'/0 l7)3KLM &(VK-@*0*=*G*G*IF;'+ T^ 

 / gY6XYZ3 ^ 

 U CZg T6 << #FkQSUXYZU[S]^	Y	"" #A7BPSTUPVjY	Y	"" #@'2sSTvZX	Y	""	#O ^ 

	 4

	    E 1ED/#A-E	E !D43EE D; /E4D8;D?
EE H> E H'1FH%1GH#1HHH> %H6 6H:>I II II opportunity_numberc                    SnSn [        5       nU(       d  [        S5      eU   UR                  5       nUR                  X45        UR                   Vs/ s H  oDS   PM	     nnUR                  5       nU(       aw  [        [        XV5      5      n[        R                  SU  S35        SU;   a   US   (       a  US   R                  5       US'   UsSSS5        U(       a   UR                  5         $ $ [        R                  SU  S35         SSS5        U(       a   UR                  5         ggs  snf !    $ = f!    g= f! , (       d  f       O= f! [        R                   a6  nS	U  S
[        U5       3n	[        R!                  U	5        [#        U	5      eSnAf[         a6  nSU  S
[        U5       3n	[        R!                  U	5        [        U	5      eSnAf["         a6  nSU  S
[        U5       3n	[        R!                  U	5        [#        U	5      eSnAff = f U(       a   UR                  5         g!    g= fg! U(       a   UR                  5         f !    f = ff = f)uk  
Busca una oportunidad en la tabla Q_OpportunityCRM

Args:
    opportunity_number (float): Número de oportunidad del CRM

Returns:
    Dict: Diccionario con los datos de la oportunidad si se encuentra, None si no existe
    
Raises:
    ConnectionError: Si no se puede establecer conexión con la base de datos
    Exception: Para otros errores de base de datos
a<  
        SELECT TOP 1
            Q_CRM.CRM_OpportunityNumber,
            Q_CRM.CRM_Version,
            Q_CRM.CRM_ContactID,
            Q_CRM.CRM_ContactName,
            Q_CRM.CRM_ContactType,
            Q_CRM.CRM_AssignedSalesperson,
            Q_CRM.CRM_ContactAdress,
            Q_CRM.CRM_ContactColonia,
            Q_CRM.CRM_ContactCity,
            Q_CRM.CRM_ContactNumber,
            Q_CRM.CRM_ContactCountry,
            Q_CRM.CRM_ContactLegalIdentifier,
            Q_CRM.CRM_ContactZip,
            Q_CRM.CRM_ContactState,
            Q_CRM.CRM_ContactEmail,
            Q_CRM.Status,
            Q_CRM.CreatedAt,
            Q_CRM.CreatedBy,
            Q_CRM.Active
        FROM Q_CRM
        WHERE Q_CRM.CRM_OpportunityNumber = ?
            AND Q_CRM.Active = 1
        ORDER BY Q_CRM.CRM_Version DESC
    Nr   r   r   r   r   r   z-Error de base de datos al buscar oportunidad rJ   u)   Error de conexión al buscar oportunidad z'Error inesperado al buscar oportunidad r   )
r   r3   r%   r   r   rj   rY   rk   r+   r   s
             r,   buscar_oportunidad_crmr   _  s[   E6 D9!"XYY[[]FNN5"78 06/A/AB/AVay/AGB //#Cc'/0 l+=*>>VWX &(VK-@*0*=*G*G*IF;'+ T^ 

 / .@-AAcde3 ^ 

 U CZg T6 << #CDVCWWYZ]^_Z`Yab	Y	"" )?@R?SSUVYZ[V\U]^	Yi(( #=>P=QQSTWXYTZS[\	Y	""	#O ^ 

	 4

	 r   c           	      ,   SnSn [        5       nU(       d  [        S5      eU   UR                  5       nUR                  X45        UR                   Vs/ s H  oDS   PM	     nnUR                  5       nU(       a  [        [        XV5      5      nUR                  SS5      R                  5       nUS;   a*  [        R                  SU  S	U S
35        [        SU 35      eUS;   a*  [        R                  SU  S	U S35        [        SU 35      e[        R                  SU  SUS    SU 35        UsSSS5        U(       a   UR                  5         $ $ [        R                  SU  S35         SSS5        U(       a   UR                  5         ggs  snf !    $ = f!    g= f! , (       d  f       O= f! [         a  n	U	eSn	A	f[        R                    a6  n	SU  S[#        U	5       3n
[        R%                  U
5        ['        U
5      eSn	A	f[&         a6  n	SU  S[#        U	5       3n
[        R%                  U
5        ['        U
5      eSn	A	ff = f U(       a   UR                  5         g!    g= fg! U(       a   UR                  5         f !    f = ff = f)u  
Verifica si ya existe una oportunidad y retorna la versión más alta
También valida que no esté cerrada/vendida o en proceso activo

Args:
    opportunity_number (str): Número de oportunidad del CRM

Returns:
    Dict: Datos de la oportunidad existente con versión más alta, None si no existe
    
Raises:
    Exception: Si la oportunidad está cerrada/vendida o en proceso activo
a  
        SELECT TOP 1
            Q_OpportunityCRM.CRM_OpportunityNumber,
            Q_OpportunityCRM.Version,
            Q_OpportunityCRM.CRM_OpportunityID,
            Q_OpportunityCRM.Status,
            Q_OpportunityCRM.Active
        FROM Q_OpportunityCRM
        WHERE Q_OpportunityCRM.CRM_OpportunityNumber = ?
            AND Q_OpportunityCRM.Active = 1
        ORDER BY Q_OpportunityCRM.Version DESC
    Nr   r   Statusr8   )VENDIDOVENDIDAATENDIDOr   z tiene status z - proceso terminadozOPPORTUNITY_CLOSED:)	PENDIENTEzEN DESARROLLOzEN APROBACIONEN_DESARROLLOEN_APROBACIONz - proceso activozOPPORTUNITY_IN_PROCESS:u    encontrada con versión r|   z
 y status z no encontradaz0Error de base de datos al verificar oportunidad rJ   z*Error inesperado al verificar oportunidad )r   r   r   r   r   r   r   r   r   upperrK   re   
ValueErrorrL   r0   rf   rg   r=   rh   r!   )r   r3   r%   r   r   rj   rY   rk   statusr+   r   s              r,   verificar_oportunidad_existenter    s   E D5!"XYY[[]FNN5"78/5/A/AB/AVay/AGB//#Cc'/0  Hb1779 ??NN\2D1E^TZS[[o#pq$':6(%CDD nnNN\2D1E^TZS[[l#mn$'>vh%GHHl+=*>>WX^_hXiWjjtu{t|}~1 TX 

 % l+=*>nMN7 X 

 Q CVa T:  << #FGYFZZ\]`ab]c\de	Y	"" #@AS@TTVWZ[\W]V^_	Y	""#M X 

	 4

	 s   F5 1F$F#CF$2	F5 FF$/F5 ?F F$FF!$
F2.F5 1I1 2F5 5
I?GI1H		I1III1 I) )I-1J:J
JJJ)r8   )General)/__doc__r   rf   loggingtypingr   r   r   r   flaskr   r	   r
   r   	getLoggerrK   configr   r   Consultas_SQL.conexionr   #App.Utilities_module.DocsManagementr   )Consultas_SQL.Utilities.DocsManagementSQLr   #App.Utilities_module.MailManagementr   r-   r5   rA   r=   rO   rZ   r_   ra   rm   rr   ru   r   r   r   intr   r   r   r   r   r   r,   <module>r     sN      . . 5 5 
		8	9 * 1 P E G L^+\"H$5s $5NT#s(^, (tDeCJ.?)?$@A ."P,`.Xvk\# >6C 6C 6tMGhtDz* MG`ke k k\du d$ dNR R Rr   