+
    2iī                     d   R t ^ RIHt ^ RIt^ RIt^ RIHtHtHtHt ^ RI	H
t
HtHtHt ]P                  ! R4      t^ RIHtHt ^ RIHt ^ RIHt ^ R	IHt ^ R
IHt ^ RI	Ht R tR tR%R R lltR R ltR R ltR t R t!R t"R t#R t$R&R R llt%R R lt&R R lt'R R  lt(R! R" lt)R# R$ lt*R# )'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                   RpRp \        4       ;_uu_ 4       pVP                  4       pVP                  W34       VP                  4       pV'       g   \	        RV  24        RRR4       R# VP
                   Uu. uF  qf^ ,          NK  	  pp\        \        Wu4      4      pVP                  W 34       VP                  4       p	V	'       d
   V	^ ,          MRVR&   \        VP                  R4      \        4      '       d   VR,          P                  R4      VR&   VuuRRR4       # u upi   + '       g   i     R# ; i  \         d   p
\	        RT  R	T
 24        Rp
?
R# Rp
?
ii ; i)
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 N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,      s=   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*:D%BD*
D> %D**D;	5D> ;D> >E%	E  E%c                 r   V '       d   V'       d   R pRp \        4       pV;_uu_ 4        VP                  4       pVP                  W0V34       VP                  4        VR;;,          RV  2,          uu&   TuuRRR4       V'       d   VP	                  4        # # R# R#   + '       g   i     MO; i  \
         d?   pRTR&   RT R\        T4       2TR&   Tu Rp?T'       d   TP	                  4        # # Rp?ii ; i T'       d   TP	                  4        R# R#   T'       d   TP	                  4        i i ; i)zi
            UPDATE Q_CostingHead
            SET DocsID = ?
            WHERE CostingID = ?
            Nmessagez" | Costing actualizado con DocsID FsuccesszError al actualizar CostingID : )r   r   r   commitcloser    str)DocsID	CostingID	resultadoqueryr$   r   r*   s   &&&    r+   uploadFileAuxiliarr8   l   s   )E
 D!%'T![[]FNN59*=>KKMi(.PQWPX,YY($ T JJL ) v T  !',	)$)G	{RTUXYZU[T\']	)$  JJL !  JJL 4JJL sS   B2 AB7
B2 B/	*B2 .D /B2 2C;=C6C;D 6C;;D D6c                $    V ^8  d   QhR\         /# )   destinatarioTestr3   )formats   "r+   __annotate__r>      s     $5 $5s $5    c                   \        V 4      p\        P                  RV 24       V'       g   \        RV  24       RRRRV  2/#  RpVR8w  d   TpM	VR,          p\	        R	R
RV./VR7      pVP                  R4      '       d   \        RVR,           24       V# \        RVP                  R4       24       V#   \         d+   p\        RT  RT 24       RRR\        T4      /u Rp?# Rp?ii ; i)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 r/   Fmensajez$No se encontraron datos para TaskID  r   z0Emails/Ingenieria/Cotiz/CotizCreatedIngMail.htmlu/   ✅ Costeo creado correctamente - IGSA ElephantTO)template_pathasuntodestinatarios_adicionalestemplate_datau"   ✅ Correo enviado exitosamente a u   ⚠️ Error al enviar correo: u'   ❌ Error al enviar correo para TaskID r0   N)r,   loggerinfor   r   r   r    r3   )r!   r;   datadestinatarior6   r*   s   &&    r+   enviarCorreoCostingCreadorL      s   
 '"D
KK%dV,-A'KL5)/ST[S\-]^^5r!+L=L+LD|o' 
	 ==##6tG}oFG  3IMM)4L3MNO 57y1#FG5)SV445s%   AB8 B8 8C-C("C-(C-c                \    V ^8  d   QhR\         \        \        \        3,          ,          /# r:   return)r   r   r3   )r=   s   "r+   r>   r>      s       T#s(^, r?   c                    Rp . p \        4       ;_uu_ 4       pVP                  4       ;_uu_ 4       pVP                  V 4       VP                  4       pV F/  pVP	                  RV^ ,          RV^,          RV^,          /4       K1  	  RRR4       RRR4       V#   + '       g   i     L; i  + '       g   i     T# ; i  \
         d   p\        RT 24       . u Rp?# Rp?ii ; i)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;CurrencyCodeFrontES
CurrSymbolNzError al obtener las monedas: )r   r   r   fetchallappendr    r   )r7   
currenciesr$   r   rowsrowr*   s          r+   get_currenciesrY      s     YEJ&u%(C%%'QCFLRUVWRXY[         .qc23	sR   C B-AB	B-	C B*%B--B>	8C >C C&C!C&!C&c                ~    V ^8  d   QhR\         \        \        \        \        \        3,          3,          ,          /# rN   )r   r   r3   r   float)r=   s   "r+   r>   r>      s+      tDeCJ.?)?$@A r?   c                   Rp. p \        4       ;_uu_ 4       pVP                  4       ;_uu_ 4       pVP                  W34       VP                  4       pV FA  pVP	                  RV^ ,          RV^,          R\        V^,          4      RV^,          /4       KC  	  RRR4       RRR4       V#   + '       g   i     L; i  + '       g   i     T# ; i  \         d   p\        RT 24       . u Rp?# Rp?ii ; i)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 = ?;TaxCoderR   	TaxAmountrQ   Nz Error al obtener los impuestos: )r   r   r   rT   rU   r[   r    r   )rQ   r7   taxesr$   r   rW   rX   r*   s   &       r+   	get_taxesr`      s     z	!!T[[]]fNN5/:!??,D#iQCFKY^FZ$%3SV&= >  $ # " L #] "! L 	4QC89I	sR   C C A*B-	C #	C -B=8C  C	C C C9C4.C94C9c                    R pRp \        4       pT;_uu_ 4        VP                  4       pVP                  W34       VP                   Uu. uF  qD^ ,          NK  	  ppVP	                  4       pV'       dT   \        \        WV4      4      p\        P                  RV  R24       TuuRRR4       V'       d    VP                  4        # # \        P                  RV  R24        RRR4       V'       d    VP                  4        R# R# u upi     # ; i    R# ; i  + '       g   i     MH; i  \        P                   d.   pRT  RT 2p	\        P                  T	4       \        T	4      hRp?ii ; i T'       d    TP                  4        R#     R# ; iR#   T'       d    TP                  4        i     i ; ii ; i)zs
        SELECT TOP 1 CRM_OpportunityID, QuotationTypeID
        FROM Q_SpQ_FormsHead
        WHERE FormID = ?
    NzFormulario z encontrado correctamente.u   No se encontró FormID .z&Error SQL en obtener_datos_formulario(r   r   r   r   r   r   r   r   rH   rI   r2   warningpyodbcErrorerrorr    )
form_idr7   r$   r   r&   columnsrX   resultr*   msgs
   &         r+   obtener_datos_formulariorl      s   E DT[[]FNN5*-)/););<);#1vv);G<//#Cc'/0k'2LMN T( 

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

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

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

	    D/ 1DDAD$
D/ 7D	D#D/ 4D DDDD,	'D/ +F ,D/ /E1(E,,E11F >F F	F=$F54F=5F97F=c                    R pRp \        4       pT;_uu_ 4        VP                  4       pVP                  W34       VP                   Uu. uF  qD^ ,          NK  	  ppVP	                  4       pV'       dT   \        \        WV4      4      p\        P                  RV  R24       TuuRRR4       V'       d    VP                  4        # # \        P                  RV  R24        RRR4       V'       d    VP                  4        R# R# u upi     # ; i    R# ; i  + '       g   i     MH; i  \        P                   d.   pRT  RT 2p	\        P                  T	4       \        T	4      hRp?ii ; i T'       d    TP                  4        R#     R# ; iR#   T'       d    TP                  4        i     i ; ii ; i)a%  
        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 = ?
    NzContexto de usuario z recuperado correctamente.%   No se encontró contexto para UserID rb   z&Error SQL en obtener_contexto_usuario(r   rc   )
user_idr7   r$   r   r&   ri   rX   rj   r*   rk   s
   &         r+   obtener_contexto_usuariorq      s   E  DT[[]FNN5*-)/););<);#1vv);G<//#Cc'/027);UVW T( 

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

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

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

	 rm   c                     \        4       p V P                  4       pRpVP                  V4       VP                  4       pV'       d   VP                  MR VP                  4        V P                  4        #   \         dM   p\        P                  R\        T4       24        Rp?XP                  4        X P                  4        R# Rp?ii ; i  XP                  4        X P                  4        i ; i)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   NextCostingNumr2   r    rH   rg   r3   )r$   r   r7   rX   r*   s        r+   generar_costing_numrt   /  s     	uoo%(s!!d2
 	

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

 	

s*   AA6 6C!C"C CC "C2c                v   RpRp V P                  R4      pV P                  R4      pV P                  R4      p\        P                  RV P                  R4       24       V'       d	   V'       gW   \        RR/4      R	3V'       d   VP	                  4        V'       d(    VP	                  4        \        P                  R
4       # # \        P                  RV RV 24       \        V4      pV'       gZ   \        RRV 2/4      R3V'       d   VP	                  4        V'       d(    VP	                  4        \        P                  R
4       # # \        V4      pV'       gZ   \        RRV 2/4      R3V'       d   VP	                  4        V'       d(    VP	                  4        \        P                  R
4       # # \        4       pVP                  4       pV P                  R^4      pVP                  R4       VP                  4       ^ ,          p	V	^,           p
V
 RV 2p\        P                  RV
 RV	 R24       Rp\        R4       VP                  V. V
NVNVP                  R4      NVNVP                  R4      NVNVP                  R4      NVP                  R4      NVP                  R4      NV P                  R4      NV P                  R4      NV P                  R4      NV P                  R 4      NV P                  R!4      NV P                  R"4      NV P                  R#4      NV P                  R$4      NV P                  R%4      NV P                  R&4      NV P                  R'4      NV P                  R(4      NV P                  R)4      NV P                  R*4      NV P                  R+4      NVP                  R,4      NV P                  R-4      NV P                  R.4      NV P                  R/4      NV P                  R04      NV P                  R14      NV P                  R24      NV P                  R3R44      NV P                  R54      NV P                  R64      N54       \        P                  R7V
 24       \        P                  R8V P                  R4       R924       V P                  R:. 4      p^ pV P                  R4      pVR;8X  d   ^pMVRd9   d   ^V P                  R^ 4      ^d,          ,           ^V P                  R!^ 4      ^d,          ,           ,          ^V P                  R#^ 4      ^d,          ,           ,          ^V P                  R%^ 4      ^d,          ,           ,          pM^p\        P                  R<VR= 24       V'       d   R>pV F  pVP                  VVVP                  R?4      VP                  R@4      VP                  RA4      VP                  RB4      VP                  RC4      VP                  RD4      V,          VP                  RD4      V,          VP                  RB4      ,          34       V^,          pK  	  \        P                  REV RF24       M\        P                  RG4       V P                  RH4      pRIpVP                  VV4       RJpVP                  VVP                  R,4      34       VP                  4       pV^ ,          pV^ 8X  dM   RKpVP                  VVP                  R,4      34       \        P                  RLVP                  R,4       RM24       \        RNV4       ROpVP                  VV34       VP                  4       pV'       d
   V^ ,          MRp\        P                  RPV RQV 24       \        P                  RRV 24       RSpVP                  VVVV34       VP                  4        \        P                  RTV 24       \        RUR4RVRWRXRVRYV
RVRVP                  R4      R,VP                  R,4      RZVR/V P                  R/4      //4      ^3V'       d   VP	                  4        V'       d(    VP	                  4        \        P                  R
4       # #     EL; i    # ; i    ELj; i    # ; i    EL; i    # ; i    La; i    # ; i  \        P                   d   pT'       d   TP!                  4        \        P#                  R[T 24       \        RR\R]\%        T4      /4      R^3u Rp?T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \        P                  R
4       #     # ; i# Rp?i\        P&                   d   pT'       d   TP!                  4        \        P#                  R_T 24       \        RR`R]\%        T4      /4      Ra3u Rp?T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \        P                  R
4       #     # ; i# Rp?i\(         d   pT'       d   TP!                  4        \        P#                  RbT 24       \        RRcR]\%        T4      /4      Ra3u Rp?T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \        P                  R
4       #     # ; i# Rp?ii ; i  T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \        P                  R
4       i     i ; ii ; i)eu   
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 r5   rg   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   ro   Versionz7SELECT ISNULL(MAX(CostingNum), 999) FROM Q_CostingHead;-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PreviusDocsIDr4   TechnicalTermsAndConditionsRunTimeNumberRunTimeTypeActiveTrQ   r]   u9   ✅ Q_CostingHead insertado correctamente con CostingNum=z----case cost: z----Q_CostingDetailAz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: r/   r.   zCosting creado exitosamenterJ   
CostingNumItemsInsertadosu   ❌ Error de integridad: zError de integridad de datosdetailsi  u   ❌ Error de base de datos: zError en base de datos  u   ❌ Error inesperado: zError al crear costing)BC)r   rH   rI   r	   r2   rl   rq   r   r   r   r   r   rd   r1   re   IntegrityErrorrollbackrg   r3   rf   r    )rJ   r$   r   rh   rp   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   F  s
   
 DFS ((8$((8$.1 	j+!6 789gG%[\]_bb~ 

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

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

45 a ((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;<>  N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
 t4Zk7!488,=#>#TXX.A%B!#3$((8,
  B 

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

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

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

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

45 sy  A)b. 0b. a3%a;?b. b&%b
$b. 9b%b9Ob. 	J"b. 3b %b'3a8;a?b
bbb b$'b+.kA	e(kk  k  d**d.:%e  e$(k?k A	h%	k
k  k  g''g+7%hh!%k2k3A	k<k=k  k  	jj*%kkkk   	l8)k:9l8:k><l8
%l0/l80l42l8c           	     l   \         P                  R4       RpRp \         P                  RV  24       V P                  R4      pV P                  R4      p\         P                  RV 24       \         P                  RV 24       V'       d	   V'       gW   \        RR	/4      R
3V'       d   VP	                  4        V'       d(    VP	                  4        \         P                  R4       # # \         P                  RV 24       \        4       pVP                  4       pRpVP                  WTV34       VP                  ^ 8X  ds   \         P                  RV R24       \        RRV 2/4      R3V'       d   VP	                  4        V'       d(    VP	                  4        \         P                  R4       # # VP                  4        \         P                  RV RV 24       \        RRRRV 2/4      ^3V'       d   VP	                  4        V'       d(    VP	                  4        \         P                  R4       # #     EL; i    # ; i    L; i    # ; i    LQ; i    # ; i  \        P                   d   pT'       d   TP                  4        \         P                  RT 24       \        RRR\        T4      /4      R3u Rp?T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \         P                  R4       #     # ; i# Rp?i\          d   pT'       d   TP                  4        \         P                  RT 24       \        RRR\        T4      /4      R3u Rp?T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \         P                  R4       #     # ; i# Rp?ii ; i  T'       d   TP	                  4        M    M; iT'       d/    TP	                  4        \         P                  R4       i     i ; ii ; i)z6
Endpoint para actualizar el DocsID en Q_CostingHead.
u8   🔄 Iniciando actualización de DocsID en Q_CostingHeadNu%   Datos recibidos para actualización: r5   r4   zcostingId recibido: zdocsId recibido: rg   u7   Faltan parámetros: CostingID y DocsID son obligatoriosry   u*   🔒 Conexión a la base de datos cerrada.u8   📋 Iniciando actualización de DocsID para CostingID: zY
        UPDATE Q_CostingHead
        SET DocsID = ?
        WHERE CostingID = ?
        u$   ⚠️ No se encontró el CostingID z para actualizar.u*   No se encontró un registro con CostingID rz   u(   ✅ DocsID actualizado correctamente a 'z' para CostingID r/   Tr.   z.DocsID actualizado correctamente en CostingID 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)rH   rI   r   r	   r2   r   r   r   rowcountrd   r1   re   rf   r   rg   r3   r    r   get_json)rJ   r$   r   r   docs_idupdate_queryr   r*   s   &       r+   update_costing_head_docsidSQLr   d  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
 tG
|T
  , 

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

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

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

HI s  A9I& I& +H;%I+BI& 5I
%I5>I& ;I%I;I I
IIII#&O;A	L OO O K""K&2%LL O-O.A	O7O8O O NN%%OOOO 	P3$O54P35O97P3%P+*P3+P/-P3c                $    V ^8  d   QhR\         /# )r:   r5   r<   )r=   s   "r+   r>   r>     s      # r?   c           	     4   \        VR\        V4      4      pVR,          p. pV  FS  p\        VVVP                  RV 2RV 2R7      p	V	R,          '       g   K4  VP	                  V	R,          R,          4       KU  	  R\        V4      ^ 8  RVR	\        V4      R
V/# )u9   
Patrón simplificado para casos con una sola categoría
Q_CostingHeadr   zArchivo de zVentas/Costeo/)file_objr   titler   ruta_completar/   rJ   download_urlarchivos_subidosurls)r   obtener_nombre_usuarior   filenamerU   len)
archivos	modulo_idrp   r5   	categoriadocs_resultr   urls_generadasarchivor6   s
   &&&&&     r+   upload_archivos_simpler     s     "'?<RSZ<[\K)$G N4""%i[1*9+6
	 Y!!)F"3N"CD  	3~&*7C/	 r?   c                0    V ^8  d   QhR\         R\        /# )r:   rp   rO   )intr3   )r=   s   "r+   r>   r>   1  s     6 6C 6C 6r?   c           	        RpRp \        4       pV'       g    RV  2V'       d    VP                  4        # # T;_uu_ 4        VP                  4       pVP                  W34       VP	                  4       pV'       dh   RP                  V^ ,          P                  4       4      pVP                  4       '       d   TMRV  2uuRRR4       V'       d    VP                  4        # # RV  2uuRRR4       V'       d    VP                  4        # #     # ; i    # ; i    # ; i  + '       g   i     Me; i  \         dU   p\        P                  RT  R\        T4       24       RT  2u Rp?T'       d    TP                  4        #     # ; i# Rp?ii ; i T'       d    TP                  4        R#     R# ; iR#   T'       d    TP                  4        i     i ; ii ; i)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_ z#Error al obtener nombre de usuario r0   )r   r2   r   r   r   joinsplitstripr    rH   rd   r3   )rp   r7   r$   r   rX   	full_namer*   s   &      r+   r   r   1  s   E  DgY'& 

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

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

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

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

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

	 s   D4 D
D4 A6D!8D!>
D4 D#D!'
D4 :DDDD!D1	,D4 0F< 1D4 4F?(F'F(F< 5FF
FF<  F2 2F7<	GGGGGc                P    V ^8  d   QhR\         \        \        ,          ,          /# rN   )r   r   r   )r=   s   "r+   r>   r>   k  s"     MG MGhtDz* MGr?   c            	     L   Rp Rp \        4       pV'       g   \        R4      hT;_uu_ 4        VP                  4       pVP                  V 4       VP                   Uu. uF  q3^ ,          NK  	  ppVP                  4       pV'       d   . pV Fa  p\        \        WG4      4      pR F3  p	W9   g   K  W,          '       g   K  W,          P                  4       W&   K5  	  VP                  V4       Kc  	  \        P                  R\        V4       R24       TuuRRR4       V'       d    VP                  4        # # \        P                  R4        RRR4       V'       d    VP                  4        R# R# u upi   \         d    \        P                  R4        # i ; i  \         d    \        P                  R4        R# i ; i  + '       g   i     M; i  \         P"                   d4   p
R\%        T
4       2p\        P'                  T4       \        T4      hRp
?
i\         d4   p
R	\%        T
4       2p\        P'                  T4       \        T4      hRp
?
i\         d4   p
R
\%        T
4       2p\        P'                  T4       \        T4      hRp
?
ii ; i T'       d;    TP                  4        R#   \         d    \        P                  R4        R# i ; iR#   T'       d9    TP                  4        i   \         d    \        P                  R4        i i ; ii ; i)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 datosz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: )	CreatedAt	UpdatedAt)r   ConnectionErrorr   r   r   rT   r   r   	isoformatrU   rH   rI   r   r2   r    rd   re   rf   r3   rg   )r7   r$   r   columnri   rW   resultsrX   rj   
date_fieldr*   	error_msgs               r+   buscar_scorer   k  s   E
 D:G!"XYYT[[]FNN5! 06/A/AB/AVayy/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   'G 0GE4.=G0G AG
G 'E99GG  F 4G9 FF GGG	G K! G J..HJ)J*.IJ%J&.JJK! &J8 8 KK!	L#+K<;L#< LL#LL#c                F    V ^8  d   QhR\         R\        \        ,          /# )r:   r!   rO   r[   r   r   )r=   s   "r+   r>   r>     s"     k ke k kr?   c                   RpRp \        4       pV'       g   \        R4      hT;_uu_ 4        VP                  4       pVP                  W34       VP                   Uu. uF  qD^ ,          NK  	  ppVP                  4       pV'       d   \        \        WV4      4      p\        P                  RV  R24       RV9   d*   VR,          '       d   VR,          P                  4       VR&   TuuRRR4       V'       d    VP                  4        # # \        P                  RV  R24        RRR4       V'       d    VP                  4        R# R# u upi     # ; i    R# ; i  + '       g   i     M; i  \        P                   d7   pRT  R	\        T4       2p	\        P!                  T	4       \#        T	4      hRp?i\         d7   pR
T  R	\        T4       2p	\        P!                  T	4       \#        T	4      hRp?i\"         d7   pRT  R	\        T4       2p	\        P!                  T	4       \#        T	4      hRp?ii ; i T'       d    TP                  4        R#     R# ; iR#   T'       d    TP                  4        i     i ; ii ; i)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   Oportunidad  encontrada exitosamenter   " no encontrada en la base de datosz0Error de base de datos al buscar la oportunidad r0   u+   Error de conexión al buscar la oprtunidad z*Error inesperado al buscar la oportunidad r   r   r   r   r   r   r   r   rH   rI   r   r2   rd   re   rf   r3   rg   r    )
r!   r7   r$   r   r   ri   rX   rj   r*   r   s
   &         r+   buscar_tarea_crmr     sR   !EF D9!"XYYT[[]FNN5*- 06/A/AB/AVayy/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

	    'E2 1EE/AEE'
E2 :EE&E2 7E EEEE/	*E2 .I$ /E2 2H;1F88H;H;1G77H;H;1H66H;;I$ I I$	J.I?>J?JJc                F    V ^8  d   QhR\         R\        \        ,          /# r:   opportunity_numberrO   r   )r=   s   "r+   r>   r>   )  s"     d du d$ dr?   c                   RpRp \        4       pV'       g   \        R4      hT;_uu_ 4        VP                  4       pVP                  W34       VP                   Uu. uF  qD^ ,          NK  	  ppVP                  4       pV'       d   \        \        WV4      4      p\        P                  RV  R24       RV9   d*   VR,          '       d   VR,          P                  4       VR&   TuuRRR4       V'       d    VP                  4        # # \        P                  RV  R24        RRR4       V'       d    VP                  4        R# R# u upi     # ; i    R# ; i  + '       g   i     M; i  \        P                   d7   pRT  R	\        T4       2p	\        P!                  T	4       \#        T	4      hRp?i\         d7   pR
T  R	\        T4       2p	\        P!                  T	4       \        T	4      hRp?i\"         d7   pRT  R	\        T4       2p	\        P!                  T	4       \#        T	4      hRp?ii ; i T'       d    TP                  4        R#     R# ; iR#   T'       d    TP                  4        i     i ; ii ; i)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   z-Error de base de datos al buscar oportunidad r0   u)   Error de conexión al buscar oportunidad z'Error inesperado al buscar oportunidad r   )
r   r7   r$   r   r   ri   rX   rj   r*   r   s
   &         r+   buscar_oportunidad_crmr   )  sb   E6 D9!"XYYT[[]FNN5"78 06/A/AB/AVayy/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                F    V ^8  d   QhR\         R\        \        ,          /# r   )r3   r   r   )r=   s   "r+   r>   r>     s"     R R R Rr?   c           
     r   RpRp \        4       pV'       g   \        R4      hT;_uu_ 4        VP                  4       pVP                  W34       VP                   Uu. uF  qD^ ,          NK  	  ppVP                  4       pV'       d   \        \        WV4      4      pVP                  RR4      P                  4       pVR9   d+   \        P                  RV  RV R24       \        R	V 24      hVR9   d+   \        P                  RV  RV R
24       \        RV 24      h\        P                  RV  RVR,           RV 24       TuuRRR4       V'       d    VP                  4        # # \        P                  RV  R24        RRR4       V'       d    VP                  4        R# R# u upi     # ; i    R# ; i  + '       g   i     M; i  \         d   p	T	hRp	?	i\        P                    d7   p	RT  R\#        T	4       2p
\        P%                  T
4       \'        T
4      hRp	?	i\&         d7   p	RT  R\#        T	4       2p
\        P%                  T
4       \'        T
4      hRp	?	ii ; i T'       d    TP                  4        R#     R# ; iR#   T'       d    TP                  4        i     i ; ii ; i)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   StatusrB   r   z tiene status z - proceso terminadozOPPORTUNITY_CLOSED:z - proceso activozOPPORTUNITY_IN_PROCESS:u    encontrada con versión r{   z
 y status z no encontradaz0Error de base de datos al verificar oportunidad r0   z*Error inesperado al verificar oportunidad )VENDIDOVENDIDAATENDIDO)	PENDIENTEzEN DESARROLLOzEN APROBACIONEN_DESARROLLOEN_APROBACION)r   r   r   r   r   r   r   r   r   upperrH   rd   
ValueErrorrI   r2   re   rf   r3   rg   r    )r   r7   r$   r   r   ri   rX   rj   statusr*   r   s   &          r+   verificar_oportunidad_existenter    s   E D5!"XYYT[[]FNN5"78/5/A/AB/AVayy/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   'G 1F=F)/CF=
G F.*F=G F5 )F=.F25F:=G	G J G I*GI*4I*51H&&I*3I*41I%%I**J 7J	 	J	J6J.-J6.J20J6)rB   )General)+__doc__r   re   loggingtypingr   r   r   r   flaskr   r   r	   r
   	getLoggerrH   configr   r   Consultas_SQL.conexionr   #App.Utilities_module.DocsManagementr   )Consultas_SQL.Utilities.DocsManagementSQLr   #App.Utilities_module.MailManagementr   r,   r8   rL   rY   r`   rl   rq   rt   r   r   r   r   r   r   r   r   r?   r+   <module>r     s       . . 5 5 
		8	9 * 1 P E G L^!2$5N(."P,`.[|k\>6tMG`k\dNRr?   