+
    :mai                        R t ^ RIt^ RIHt ^ RIt^ RIt^ RIHt ^ RIH	t	H
t
HtHt ^ RIHtHt ^ RIHtHtHtHtHtHtHtHtHt ^ RIHt ]! 4        R]P8                  ! R	4      R
]P8                  ! R4      R]P8                  ! R4      RR/tRRR]P8                  ! R4      R]P8                  ! R4      R]P8                  ! R4      RRRR.R]P8                  ! R4      RRR R!R"R#R$R%/tR&. R'NR(NR)NR*NR+NR,NR-NR.NR/NR0NR1NR2NR3NR4NR5NR6NR7NR8NR9NR:NR;NR<NR=NR>NR?NR@NRANRBNRCNRDNRENRFNRGNRHNRINRJNRKNRLNRMNRNNRONRPNRQNRRNRSNRTNRUNRVNRWNRXNRYNRZNR[NR\NR]NR^NR_NR`NRaNRbNRcNRdNReNRfNRgNRhNRiNRjNRkNRlNRm. RnNRoNRpNRqNRrNRsNRtNRuNRvNRwNRxNRyNRzNR{NR|NR}NR~NRNRNRNRNRNRNRNRNRNRNRNRNRNRNRNRNRNRNRNR. RO/t ! R R4      t  ! R R4      t! ! R R4      t"R t#R t$R t%R t&R t'R# )u   
Módulo completo de actualización del Score.
Maneja el flujo completo desde la validación del Excel hasta la 
actualización de la tabla CM_Score en la base de datos.
N)load_dotenvdatetime)requestjsonifyrender_templateredirect)get_connectionget_connectionERP)	obtener_datos_score_erpobtener_estructura_validadorverificar_tabla_cm_scoreobtener_ultima_version_historyobtener_columnas_tablacrear_tabla_cm_scorecrear_tabla_scorehistorytruncate_tablainsertar_bulk_score) validar_score_para_actualizacionhostFTP_HOSTuserFTP_USERpasswordFTP_PASSremote_pathW/domains/sycelephant.com/public_html/file/SyC/IngenieroControl/RespaldoActualizarScore/enabledF	tenant_id	TENANT_ID	client_idONEDRIVE_CLIENT_IDclient_secretONEDRIVE_CLIENT_SECRETredirect_urizhttp://localhost:5000/callbackscopeszFiles.ReadWrite
user_emailONEDRIVE_USERNAMEfile_id.EWDk3kJdEcdJivAlcKPi8woBTgMWG_qfQ1rYYo0Jcy5QbQ	authorityz-https://login.microsoftonline.com/{tenant_id}	graph_urlz https://graph.microsoft.com/v1.0token_cache_filezonedrive_token_cache.jsoncolumnas_erpOrderNumDepartamentoVendedorName
TotalLines	OrderLinePartNumLineDesc	CapacidadVoltajeTipoRevisionNumDecriptionProdCaseta	OrderDate
NeedByDateFechaAnticipoLiberacionCXPTerminosOrderNum&Line	ProjectIDJobNum2Revision_OVRevision_ProjectRevision_JobFechaDeCierreInsumosDemandadosInsumosEmitidosFechaDeTermino	PartNum_MDescription_MPartClass_MRefCategory_MQtyPer_MIssuedQty_MDemandado_MOnhandQty_MEn_PO_MNoPO_MEnRequisicion_MNoRequisicion_M	PartNum_GDescription_GPartClass_GRefCategory_GQtyPer_GIssuedQty_GDemandado_GOnhandQty_GEn_PO_GNoPO_GEnRequisicion_GNoRequisicion_G	PartNum_TDescription_TPartClass_TRefCategory_TQtyPer_TIssuedQty_TOnhandQty_T	PartNum_RDescription_RPartClass_RRefCategory_RQtyPer_RIssuedQty_ROnhandQty_RComentarioLINEOvEEUUClienteEEUUcolumnas_manualProdCodeComentarioSyCChkFechaSyCConsideradaPreasignacionConsideradaMaterialesu   FechaProducciónu   ComentarioProducciónu   AvanceProducciónu   FechaPlaneaciónEstadoFechau   ComentarioPlaneaciónu   FechaInicialPlaneaciónMotivoInicialFechaMatAvisoDeTerminacionTerminadoConFaltanteu   FechaActualizaciónMaterialFaltante	Auxiliar1	Auxiliar2ComentarioCalidadPreasignado_M	NoSerie_MReq_M	Comment_M
FechaReq_MPO_MCantidad_Pedida_MPreasignado_G	NoSerie_GReq_G	Comment_G
FechaReq_GPO_GCantidad_Pedida_Gcolumnas_formuladoc                   B   a  ] tR t^zt o RtR tR tR tR tR t	Rt
V tR# )	
FTPManageru   
Gestiona la conexión y operaciones con el servidor FTP
para guardar respaldos del archivo Excel

✅ Versión actualizada con navegación paso a paso
c                    \         R ,          V n        \         R,          V n        \         R,          V n        \         R,          V n        RV n        R# )r   r   r   r   N)
FTP_CONFIGr   r   r   r   ftpselfs   &cc:\Users\victor.cervantes\Desktop\Elep\src\App\SupyCtrol_Module\IngenieroControl\ActualizarScore.py__init__FTPManager.__init__   s:    v&	v&	":.%m4    c                `    \        R4       \        P                  ! V P                  4      V n        V P                  P                  V P                  V P                  4       \        R4       R#   \         d4   p\        R\        T4       24       \        R\        T4       24      hRp?ii ; i)u   
Establece conexión con el servidor FTP

Returns:
    bool: True si la conexión fue exitosa
    
Raises:
    Exception: Si hay error de conexión
zConectando a FTP...u    ✓ Conectado a FTP exitosamenteTu   ✗ Error al conectar a FTP: u   Error de conexión FTP: N)
printftplibFTPr   r   loginr   r   	Exceptionstr)r   es   & r   conectarFTPManager.conectar   s    	A'(zz$)),DHHHNN499dmm445 	A1#a&:;6s1vh?@@	As   A+A/ /B-:.B((B-c                	    \        R4        V P                  P                  R4       \        R4        T P                  P                  R4       \        R4        T P                  P                  R	4       \        R
4        T P                  P                  R4       \        R4        T P                  P                  R4       \        R4       . pT P                  P	                  RTP
                  4       \        RRP                  TR,          4       24       RT9   g   RT9   d   \        R4       M\        R4       \        R4         T P                  P                  R4       \        R4        T P                  P                  R 4       \        R!4        T P                  P                  R%4       \        R&4       T P                  P                  4       p\        R*4       \        R+T 24       Y0P                  8X  d   \        R,4       R0# \        R-4       \        R.T P                   24       \        R/T 24       R0#     EL; i  \         d   p\        RT 24        Rp?R# Rp?ii ; i  \         d   p\        RT 24        Rp?R# Rp?ii ; i  \         d   p\        RT 24        Rp?R# Rp?ii ; i  \         d   p\        RT 24        Rp?R# Rp?ii ; i  \        P                   d     T P                  P                  R4       T P                  P                  R4       T P                  P                  R4       \        R4        EL  \         d   p\        RT 24        Rp? R# Rp?ii ; ii ; i  \        P                   d     T P                  P                  R4       T P                  P                  R"4       T P                  P                  R"4       \        R#4        ELd  \         d   p\        R$T 24        Rp? R# Rp?ii ; ii ; i  \        P                   d     T P                  P                  R 4       T P                  P                  R'4       T P                  P                  R'4       \        R(4        EL  \         d   p\        R)T 24        Rp? R# Rp?ii ; ii ; i  \         d#   p\        R1\        T4       24        Rp?R# Rp?ii ; i)2u   
Crea la estructura de carpetas navegando paso a paso

Se asegura de estar en el file correcto (el que tiene Formatos, Operaciones)

Returns:
    bool: True si la estructura está lista
z,Verificando estructura de carpetas en FTP.../u     ✓ En la raíz FTPz/domainsu     ✓ En /domainsu%     ✗ No se pudo acceder a /domains: NFz/domains/sycelephant.comu!     ✓ En /domains/sycelephant.comu,     ✗ No se pudo acceder a sycelephant.com: z$/domains/sycelephant.com/public_htmlu-     ✓ En /domains/sycelephant.com/public_htmlu(     ✗ No se pudo acceder a public_html: z)/domains/sycelephant.com/public_html/fileu2     ✓ En /domains/sycelephant.com/public_html/fileNLSTu     📂 Contenido actual: z, N   NFormatosOperacionesuJ     ✓ Confirmado: Estamos en el file CORRECTO (tiene Formatos/Operaciones)u:     ⚠ ADVERTENCIA: Este file no tiene Formatos/Operacionesu&     ⚠ Puede que sea el file equivocadou!     ✗ No se pudo acceder a file: z-/domains/sycelephant.com/public_html/file/SyCu     ✓ Carpeta existe: SyCSyCu     ✓ Carpeta creada: SyCu     ✗ No se pudo crear SyC: z>/domains/sycelephant.com/public_html/file/SyC/IngenieroControlu&     ✓ Carpeta existe: IngenieroControlIngenieroControlu&     ✓ Carpeta creada: IngenieroControlu)     ✗ No se pudo crear IngenieroControl: r   u-     ✓ Carpeta existe: RespaldoActualizarScoreRespaldoActualizarScoreu-     ✓ Carpeta creada: RespaldoActualizarScoreu0     ✗ No se pudo crear RespaldoActualizarScore: u    ✓ Estructura de carpetas listau   ✓ Ubicación actual: u+   ✓ CONFIRMADO: Estamos en la ruta correctau>   ⚠ ADVERTENCIA: Ubicación actual no coincide con remote_pathz   Esperado: z   Actual:   Tu+   ✗ Error al crear estructura de carpetas: )r   r   cwdr   	retrlinesappendjoinr   
error_permmkdpwdr   r   )r   r   itemsubicacion_actuals   &   r   crear_estructura_carpetas$FTPManager.crear_estructura_carpetas   s   s	@AS!-/
Z()+789;CDEGHIJL ""65<<81$))E"I2F1GHI&-5*@fhVXBD!LM13!]^>@!vwEG  $xx||~46+,<+=>?  #3#33CE 	 VXd&6&6%789&6%789Q  =aSAB  DQCHI  @DE,  9!=> $$ !!HHLL!LMHHLL'HHLL'57  !8<= !! $$ !!HHLL!PQHHLL!34HHLL!34BD  !EaSIJ !! $$ !!HHLL!abHHLL!:;HHLL!:;IK  !LQCPQ !!2  	?AxHI	s  S &I &I &I4 &J *BK 5S 6K S &K) 5&N
 &P+ AS 1S I
S I1I,&S ,I11S 4J?JS JS J?&J:4S :J??S K&K!S !K&&S )N?AMS N)M>7N;S >NNS 
P( AO?<S ?P$
PP(S P$$P((S +S	AR S  S+S 9S	=S  SS		S S9S44S9c                     V P                   f   V P                  4        V P                  4        \        R4       \        RV 24       \        RV P                   24       \        VR4      ;_uu_ 4       pV P                   P                  RV 2V4       RRR4       \        RV 24       \        RV R	24       R
#   + '       g   i     L/; i  \         d4   p\        R\        T4       24       \        R\        T4       24      hRp?ii ; i)u   
Sube un archivo al servidor FTP

Args:
    local_path (str): Ruta local del archivo
    remote_filename (str): Nombre del archivo en el servidor
    
Returns:
    bool: True si se subió correctamente
    
Raises:
    Exception: Si hay error al subir
Nz
Subiendo archivo a FTP...z  Archivo: z  Destino: rbzSTOR u!   ✓ Archivo subido exitosamente: uT   ✓ URL: https://sycelephantt.com/file/SyC/IngenieroControl/RespaldoActualizarScore/
Tu"   ✗ Error al subir archivo a FTP: zError al subir a FTP: )	r   r   r   r   r   open
storbinaryr   r   )r   
local_pathremote_filenamefiler   s   &&&  r   subir_archivoFTPManager.subir_archivo  s    	?xx **,/1K012K 0 0123j$''4##eO+<$=tD ( 5o5FGHhixhyy{|} ('  	?6s1vh?@4SVH=>>	?s0   A3C 5 B<%C <C	C D.DDc                     V P                   e(   V P                   P                  4        \        R4       R# R#     T P                   e   T P                   P                  4         R#  R#      R# ; i; i)u   Cierra la conexión FTPNu   ✓ Desconectado de FTP)r   quitr   closer   s   &r   desconectarFTPManager.desconectar@  s^    		xx#/0 $	88'HHNN$ (s    39 A3(A**A0,A30A3)r   r   r   r   r   N)__name__
__module____qualname____firstlineno____doc__r   r   r   r   r   __static_attributes____classdictcell____classdict__s   @r   r   r   z   s,     A(||#?J r   r   c                   H   a  ] tR tRt o RtR tR tR tR tR t	R t
R	tV tR
# )OneDriveManageriR  u   
Gestiona actualización automática del Score en SharePoint
usando Microsoft Authentication Library (MSAL) con Azure AD

✅ Maneja refresh tokens automáticamente
✅ Guarda tokens en archivo local
✅ No requiere regenerar tokens manualmente
c                   ^ RI p^ RIp\        V n        V P                  R,          V n        V P                  R,          P                  V P                  R,          R7      pVP                  V P                  R,          V P                  R,          VR7      V n        V P                  R	,          V n        V P                  4       V n
        R# )
    Nr+   r*   r   r   r    r"   r    client_credentialr*   r,   )msalosONEDRIVE_CONFIGconfigr+   formatConfidentialClientApplicationmsal_appr,   _load_token_cachetoken_cache)r   r   r   r*   s   &   r   r   OneDriveManager.__init__\  s    %[1 KK,33kk+. 4 
	 ::kk+."kk/: ; 
 !%,> ?113r   c                   ^ RI p^ RIpVP                  P                  V P                  4      '       d<    \        V P                  R4      ;_uu_ 4       pVP                  V4      uuRRR4       # / #   + '       g   i     / # ; i   / u # ; i)z"Carga tokens guardados del archivoNr)r   jsonpathexistsr,   r   load)r   r   r   fs   &   r   r   !OneDriveManager._load_token_cacher  sp    77>>$//00$//5599Q< 65 		 65 		s)   B A1$
B 1B	<B B Bc                $   ^ RI p \        V P                  R4      ;_uu_ 4       pVP                  W4       RRR4       \	        RV P                   24       R#   + '       g   i     L*; i  \
         d   p\	        RT 24        Rp?R# Rp?ii ; i)zGuarda tokens en archivo localNwu     ✓ Tokens guardados en u)     ⚠️ No se pudo guardar token cache: )r   r   r,   dumpr   r   )r   token_responser   r   r   s   &&   r   _save_token_cache!OneDriveManager._save_token_cache  sw    	Cd++S11Q		., 2.t/D/D.EFG 21  	C=aSABB	Cs-   A+ A A+ A(	#A+ +B6B

Bc                   V P                   '       d0   RV P                   9   d   \        R4       V P                   R,          # V P                   '       d   RV P                   9   dw   \        R4       V P                  P                  V P                   R,          V P                  R,          R7      pRV9   d&   \        R4       V P                  V4       VR,          # \        R4      h)	ui   
Obtiene access token (renueva automáticamente si es necesario)

Returns:
    str: Access token válido
access_tokenu     ✓ Usando token del cacherefresh_tokenz&  Renovando token con refresh_token...r%   )r   r%   u!     ✓ Token renovado exitosamenteu   
❌ NO HAY TOKEN VÁLIDO

Necesitas autorizar la aplicación primero.
Ejecuta el script de autorización: python autorizar_onedrive.py
O visita: http://localhost:5000/login)r   r   r   acquire_token_by_refresh_tokenr   r   r   )r   results   & r   obtener_access_token$OneDriveManager.obtener_access_token  s     $2B2B B01##N33 43C3C C:;]]AA"..?{{8, B F
 '9:&&v.n-- 4
 	
r   c                   ^ RI pRRV 2/pV P                  R,          pV P                   RV 2pVP                  WS^R7      pVP                  ^8X  d0   VP                  4       p\        RVP                  R4       24       V# VP                  R	8X  d   \        R
4      h\        RVP                   RVP                   24      h)u    Obtiene información del archivoNAuthorizationBearer r(   /me/drive/items/)headerstimeoutu     ✓ Archivo: namei  z3Archivo no encontrado. Verifica file_id y permisos.zError al buscar archivo:  - )	requestsr   r+   getstatus_coder   r   r   text)r   r   r  r   r(   urlresponsedatas   &&      r   buscar_archivoOneDriveManager.buscar_archivo  s     w|n5
 ++i(  0	:<<b<A3&==?DODHHV$4#567N!!S(QSS78L8L7MSQYQ^Q^P_`aar   c                    ^ RI pRRV 2RR/pV P                   RV R2pVP                  VVV^xR7      pVP                  R9   d   R	# \	        R
VP                   RVP
                   24      h)z.Actualiza el contenido del archivo en OneDriveNr   r   zContent-TypezAapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetr   z/content)r   r	  r   TzError al actualizar archivo: r  )      )r  r+   putr  r   r  )r   r   r(   excel_bytesr  r   r  r  s   &&&&    r   actualizar_archivo"OneDriveManager.actualizar_archivo  s     w|n5_

   0	B<<	   
 :-;H<P<P;QQTU]UbUbTcdeer   )r   r+   r   r   r,   N)r   r   r   r   r   r   r   r   r   r
  r  r   r   r   s   @r   r   r   R  s2     4,	C 
Db,f fr   r   c                   x   a  ] tR tRt o RtR tR tR tR tR t	R t
R	 tR
 tR tR tR tR tR tR tRtV tR# )ScoreProcessori  uH   
Clase principal que maneja todo el proceso de actualización del Score
c                    RV n         RV n        \        4       V n        \        P
                  ! 4       V n        \        4       V n        RV n	        RV n
        RV n        RV n        R^ R^ R^ R^ R^ RRR	R
RR
/V n        R# )z9Inicializa el procesador con conexiones y configuracionesNfilas_excel	filas_erpfilas_procesadasfilas_eliminadasversion_historytabla_history respaldo_ftpFonedrive_actualizado)conn_erpconn_vpsr   ftp_managerr   nowtiempo_inicior   onedrive_managerdf_score_exceldf_score_erpdf_score_finalclasificacion_columnasstatsr   s   &r   r   ScoreProcessor.__init__  s    %<%\\^ / 1 # " '+# 1qRE"E	

r   c                	    \        R=4       \        R4       \        R>4       \        R4       V P                  4       pVR,          '       g	   RRRRRV/# VR	,          p\        R
V R24       \        R4        V P                  V4       RV P                  R&   \        R4       V P                  V4      V n        \        V P                  4      V P                  R&   \        RV P                  R,           R24       \        R4       V P                  4       w  rE\        R\        V P                  4       R24       \        R4       V P                  4        \        RV P                  R,           R24       \        R4       V P                  4        \        R4       \        R4        \        4       V n        \        V P                  4      V P                  R&   \        RV P                  R,           R24       \        R-4       T P                  4        \        T P                  4      T P                  R.&   T P                  R,          T P                  R.,          ,
          T P                  R/&   \        R0T P                  R.,           R24       \        R14       T P!                  4        \        R24       \        R34       T P#                  4        \        R44       \        R54       \$        R6,          '       d"    T P'                  4        RT P                  R7&   M\        R94       \        R:4       T P)                  4       p\        R=4       \        R;4       \        R>4       T#   \         d.   p\        R\        T4       24       \        R4        Rp?ELRp?ii ; i  \         dO   p\        T4      pR T9   g   R!T9   d   R"pMR#T9   d   R$pMR%T9   d   R&pMR'pRRRR(R)R*RR)TR+TR,. R?O//u Rp?# Rp?ii ; i  \         d=   p\        R8\        T4       24       \        R4       RT P                  R7&    Rp?ELRp?ii ; i  \         d2   p\        R<\        T4       R24       RRR\        T4      /u Rp?# Rp?ii ; i)@us   
Ejecuta el flujo completo de actualización del Score

Returns:
    dict: Resultado del proceso con estadísticas
r   u-   INICIANDO PROCESO DE ACTUALIZACIÓN DEL SCOREz1PASO 1/11: Validando Excel con ValidarScore.py...successFerroru   Validación de Excel fallidadetallesruta_archivou"   ✓ Validación exitosa. Archivo: z'PASO 2/11: Guardando respaldo en FTP...Tr  u1   ⚠ Warning: No se pudo guardar respaldo en FTP: zContinuando proceso...
Nz'PASO 3/11: Leyendo Excel a DataFrame...r  u   ✓ Excel leído: z filas
z+PASO 3.5/11: Aplicando limpieza de datos...u   ✓ Limpieza aplicada: z filas resultantes
z0PASO 4/11: Guardando respaldo en ScoreHistory...u   ✓ Respaldo guardado en r  z1PASO 5/11: Obteniendo estructura del validador...u&   ✓ Estructura obtenida y clasificada
z&PASO 6/11: Obteniendo datos del ERP...r  u   ✓ Datos ERP obtenidos: u   ERROR DE CONEXIÓN AL ERPu   ERROR CRÍTICOservidor_inaccesibleTIMEOUTr   u   AUTENTICACIÓNautenticacionerror_desconocidoz No se pudo obtener datos del ERP
tipo_errorconexion_erp_fallidaerror_tecnicosugerenciasz"PASO 7/11: Mezclando DataFrames...r  r  u   ✓ Mezcla completada: z(PASO 8/11: Verificando tabla CM_Score...u   ✓ Tabla CM_Score lista
z#PASO 9/11: Actualizando CM_Score...u&   ✓ CM_Score actualizada exitosamente
z/PASO 10/11: Actualizando archivo en OneDrive...r   r  u-   ⚠ Warning: No se pudo actualizar OneDrive: u-   ⚠ OneDrive deshabilitado en configuración
z"PASO 11/11: Generando respuesta...zPROCESO COMPLETADO EXITOSAMENTEu   
✗ ERROR EN EL PROCESO: zQ
================================================================================zQ================================================================================
)u<   Verifique que el servidor SQL Server del ERP esté encendidoz-Verifique conectividad de red al servidor ERPz2Verifique que SQL Server acepte conexiones remotasu*   Verifique que Named Pipes esté habilitadoz*Contacte al administrador del servidor ERP)r   validar_excelguardar_respaldo_ftpr)  r   r   leer_excel_a_dataframer%  lenaplicar_tratamiento_datosguardar_respaldo_bdobtener_estructura_columnasr   r&  mezclar_dataframesr'  !crear_tabla_cm_score_si_no_existeactualizar_tabla_cm_scorer   actualizar_onedrivegenerar_respuesta_exitosa)	r   resultado_validacionr/  r   stats_tratamientoerrores_filas_eliminadas	error_msgr4  	respuestas	   &        r   ejecutar_actualizacion_completa.ScoreProcessor.ejecutar_actualizacion_completa  sE   E	- AB-  EF#'#5#5#7 '	22u; 4  0?L6|nBGH ;<2)),7-1

>* ;<"&"="=l"KD(+D,?,?(@DJJ}%&tzz-'@&AJK ?@:>:X:X:Z7+C0C0C,D+EEYZ[ DE$$&-djj.I-J"MN EF,,.;< :;!$;$=!*-d.?.?*@

;'1$**[2I1J(STB 67##%-01D1D-EDJJ)*-1ZZ-FTfIg-gDJJ)*+DJJ7I,J+K8TU <=224./ 78**,;< CDy))?,,.9=DJJ56 FG 67668I- 34- K  2I#a&RS0112@  F	 /);?OS\?\!7J)+!*J%2!0J!4J u? "8$j'% (
!	 l ! ?I#a&RS459>DJJ56?"  	/Axr:;5Q 	s   AQ #Q 9 M" DQ (AN 9C?Q 9O9 A	Q "N-"NQ NQ O6(AO1+O6,Q 1O66Q 9Q 1P;5Q ;Q  Q Q?&Q:4Q?:Q?c                D    \        R4       V P                  P                  4       p\        R4       \        R4       V P                  P                  V4      p\        R4       \        R4       V P	                  V P
                  4      p\        R\        V4      R R24       \        R	4       V P                  P                  WV4       \        R
4       \        R\        V P
                  4       R24       R#   \         d   p\        R\        T4       24      hRp?ii ; i)z2
Actualiza archivo en OneDrive con df_score_final
z  Obteniendo access token...u     ✓ Token obtenidoz   Buscando archivo Score.xlsx...u     ✓ Archivo encontradoz   Generando Excel actualizado...u     ✓ Excel generado (,z bytes)z   Subiendo archivo a OneDrive...u%     ✓ Archivo actualizado en OneDriveu     ✓ z filas escritas
TzError al actualizar OneDrive: N)
r   r$  r   r
  dataframe_a_excel_bytesr'  r;  r  r   r   )r   r   r(   r  r   s   &    r   rB  "ScoreProcessor.actualizar_onedrive  s   	G0100EEGL() 46++::<HG,. 4566t7J7JKK*3{+;A*>gFG 45!!44\KX9:F3t22344EFG 	G<SVHEFF	Gs   C4C8 8DDDc                    ^ RI Hp V! 4       p\        P                  ! VRR7      ;_uu_ 4       pVP	                  VRRR7       RRR4       VP                  ^ 4       VP                  4       #   + '       g   i     L1; i)z1
Convierte DataFrame a bytes de Excel en memoria
)BytesIOopenpyxl)engineCM_ScoreF)
sheet_nameindexN)iorP  pdExcelWriterto_excelseekread)r   dfrP  outputwriters   &&   r   rM  &ScoreProcessor.dataframe_a_excel_bytes  s\     	 ^^F:66&KK:UKC 7 	A{{}	 76s   A,,A<	c           
     t     \        4       pV#   \         d   pRRRRRR\        T4       2/u Rp?# Rp?ii ; i)uc   
Valida el Excel usando el módulo ValidarScore.py

Returns:
    dict: Resultado de la validación
r,  Fstatus  mensajeu   Error al invocar validación: N)r   r   r   )r   	resultador   s   &  r   r8  ScoreProcessor.validar_excel  sJ    
	8:I 	5#;CF8D 	s    7277c                    \        4       pV P                  P                  W4       V P                  P                  4        R# )zj
Guarda respaldo del Excel en el servidor FTP

Args:
    ruta_archivo (str): Ruta del archivo a respaldar
N)generar_nombre_respaldor!  r   r   )r   r/  nombre_respaldos   && r   r9  #ScoreProcessor.guardar_respaldo_ftp  s2     23&&|E$$&r   c                d	   ^ RI Hp \        R4       RpV! VRR7      pW4P                  9  d-   VP	                  4        \        RV RVP                   R24      hWC,          p. pR	p\        VP                  RR
7      4       Fa  w  rV^ 8X  dE   \        V	4       U
Uu. uF+  w  rV'       d   \        V4      P                  4       MRV
 2NK-  	  pp
pKP  VP                  V	4       Kc  	  VP	                  4        \        P                  ! WgR7      p\        R\        V4       R\        VP                  4       R24       \        R,          \        R,          ,           \        R,          ,           p\        R\        V4       24       \        R\        VP                  4       24       \!        V4      \!        VP                  4      ,
          pV'       dy   \        R4       \#        V4      R,           F  p\        RV 24       K  	  \        V4      ^
8  d    \        R\        V4      ^
,
           R24       \        R\        V4       24      h\!        VP                  4      \!        V4      ,
          pV'       dz   \        R\        V4       R24       \#        V4      R,           F  p\        RV 24       K  	  \        V4      ^
8  d    \        R\        V4      ^
,
           R24       \        R4       W,          p\        R\        VP                  4       R24       \        R,          p\        R 4       VR!,           F  pWP                  9   g   K  W,          P%                  4       P'                  4       pW,          P)                  4       P'                  4       p\        V4      pV^ 8  d   \        R"V R#V R$V R%V R&2	4       K  \        R'V R#V R$V R(24       K  	  \        V4      ^8  d    \        R)\        V4      ^,
           R*24       R+VP                  9   dz   WR+,          P)                  4       ,          R+,          P+                  ^4      p\        V4      ^ 8  d7   \        R,4       VP-                  4        F  w  pp\        R-V R#V 24       K  	  \        4        \/        V4      pV# u upp
i ).u   
Lee el archivo Excel y lo convierte a DataFrame

- Lee valores calculados (data_only=True) en lugar de fórmulas

Args:
    ruta_archivo (str): Ruta del archivo Excel
    
Returns:
    pd.DataFrame: DataFrame con los datos del Excel (123 columnas)
)load_workbooku0     Leyendo Excel (solo valores, sin fórmulas)...rS  T)	data_onlyu   ❌ La hoja 'z/' no existe en el Excel.
   Hojas disponibles: zI
   Verifica el nombre de la hoja en ValidarScore.py y ActualizarScore.pyN)values_onlyColumn_)columnsu     ✓ Excel leído: z filas, z	 columnasr-   rt   r   u     🔍 Columnas esperadas: u     🔍 Columnas en Excel: u(   
  ❌ ERROR: Faltan columnas esperadas::N
   Nz      - z      ... y u    columnas mászDEl Excel no tiene todas las columnas esperadas.
Columnas faltantes: u&   
  ⚠️  Columnas extra detectadas (z):u)     ℹ️  Estas columnas serán ignoradasu     ✓ DataFrame filtrado a z columnas esperadas
u2     🔍 Verificando valores en columnas formuladas:r   u       ⚠️  z: r   z
 valores (z NULL)u       ✓ z valoresz
    ... y u    columnas formuladas másFechaMGu&   
  📅 Ejemplo de valores en FechaMG:z	    Fila )rQ  rk  r   
sheetnamesr   r   	enumerate	iter_rowsr   stripr   rW  	DataFramer;  ro  CLASIFICACION_COLUMNASsetsortedisnasumnotnaheadr   limpiar_dataframe_para_sql)r   r/  rk  NOMBRE_HOJA_EXCELwbwsr	  r   idxrowicellr\  columnas_esperadascolumnas_faltantescolcolumnas_extracolumnas_formuladasvalores_nullvalores_no_nulltotalvalores_ejemplovals   &&                     r   r:  %ScoreProcessor.leer_excel_a_dataframe  sP    	+@A
 ' <48 MM1HHJ 12 3))+ 8[\  " !",,4,"@AHCaxW`adWefWeGA3t9??,GA3-GWef C  B 	
 \\$0$SWIXc"**o5FiPQ #>2"#456"#789 	 	+C0B,C+DEF*3rzz?*;<= !!34s2::F=?01#66&' 7%&+S);%<r%A$B.QR''*+='>&?A  RZZ3/A+BB;C<O;PPRSTn-c22&' 3>"R'S%82%=$>nMN=? #+C

O+<<QRS
 55IJBC&r**Cjj !w||~113"$'--/"5"5"7B!#LR/@%
S_R``fghHSEO+<AeWHMN + "#a'Js#67!;<<UVW 

" I!4!4!67	BGGJO?#a'?A / 5 5 7HCIcU"SE23 !8 	
 (+	q gs   1R,c                j    ^ RI Hp \        V P                  4      pV! V P                  4      w  V n        p\        V P                  4      pW$,
          p\	        RV R24       RVRVRV/pWc3#   \
         d:   p\	        R\        T4       24       ^ RIpTP                  4        R. 3u Rp?# Rp?ii ; i)	uV   
Aplica limpieza y tratamiento al DataFrame usando la función de ValidarScoreHelpers
)limpiar_dataframeu   ✓ Tratamiento aplicado: z filas eliminadasfilas_originalesfilas_finalesr  zError al aplicar tratamiento: N)	9App.SupyCtrol_Module.IngenieroControl.ValidarScoreHelpersr  r;  r%  r   r   r   	traceback	print_exc)	r   r  r  filas_eliminadas_detaller  r  estadisticasr   r  s	   &        r   r<  (ScoreProcessor.aplicar_tratamiento_datos_  s    	c"4#6#67<MdNaNa<b9D!9 3 34M/?./?.@@QRS #$4"$4L  99 	23q6(;<!8O		s   A+A. .B29.B-'B2-B2c                   \        4       pV^ 8X  d   ^pRpMWRV 2p\        V4      pV P                  P                  P	                  4       p\        We4      pV'       d   TpRpMV^,           pRpRV 2pV'       d   \        4       p	\        W)4       M\        V4       \        V P                  V4       W P                  R&   WP                  R&   R# )zH
Guarda respaldo del Excel en ScoreHistory_X con versionado inteligente
TScoreHistory_Fr  r  N)r   r   r%  ro  tolistcomparar_estructura_columnasr   r   r   r   r)  )
r   version_actualversion_usarcrear_nuevatabla_actualcolumnas_historycolumnas_excelson_igualestabla_destino
estructuras
   &         r   r=  "ScoreProcessor.guardar_respaldo_bd}  s    
 89QLK +>*:;L5lC!0088??AN6~XK-#  .1" (~657J$\> =) 	D//? )5

$%&3

?#r   c                   \         V n        \        R\        V P                  R,          4       24       \        R\        V P                  R,          4       24       \        R\        V P                  R,          4       24       R# )uX   
Obtiene y asigna la clasificación de columnas (ahora usando clasificación estática)
z  - Columnas ERP: r-   z  - Columnas Manual: rt   z  - Columnas Formuladas: r   N)rw  r(  r   r;  r   s   &r   r>  *ScoreProcessor.obtener_estructura_columnas  sq    
 '=#"3t'B'B>'R#S"TUV%c$*E*EFW*X&Y%Z[\)#d.I.IJ^._*`)abcr   c                `   . pV P                   R,          pV P                   R,          pV P                   R,          p\        R\        V P                  4       R24       V P                  P	                  4        F  w  rVVR,          p/ pV F  p	WP
                  9   g   K  Wi,          W&   K!  	  V P                  V P                  R,          V8H  ,          p
\        V
4      ^ 8  d_   V
P                  ^ ,          p
V F  p	WP
                  9   g   K  W,          W&   K!  	  V F  p	WP
                  9   g   K  W,          W&   K!  	  MV F  p	RW&   K	  	  V F  p	RW&   K	  	  VP                  V4       K  	  \        P                  ! V4      V n        \        R\        V P                  4       R	24       R# )
u   
Mezcla df_score_excel y df_score_erp según las reglas de negocio

REGLA PRINCIPAL:
- df_score_erp "manda" → solo filas que existen en ERP
- Columnas ERP: siempre del ERP
- Columnas Manual/Formuladas: del Excel si existe, NULL si no
r-   rt   r   z  Procesando z filas del ERP...rA   Nu     ✓ Mezcla completada: z filas resultantes)r(  r   r;  r&  iterrowsrU  r%  ilocr   rW  rv  r'  )r   filas_scorer-   rt   r   r  fila_erpordernum_line
fila_scorer  
fila_excels   &          r   r?  !ScoreProcessor.mezclar_dataframes  s    22>B556GH!889MNc$"3"3455FGH!..779MC$_5M J $..(&.mJO $
 ,,##O4EJ :"'__Q/
*C...*4/
 + .C...*4/
 .
 +C&*JO + .C&*JO . z*I :N !ll;7)#d.A.A*B)CCUVWr   c                    \        4       pV'       g#   \        R4       \        4       p\        V4       R# \        R4       R# )z4
Verifica si CM_Score existe y la crea si no existe
u)     Tabla CM_Score no existe, creándola...z  Tabla CM_Score ya existeN)r   r   r   r   )r   exister  s   &  r   r@  0ScoreProcessor.crear_tabla_cm_score_si_no_existe  s1     *+=>57J ,./r   c                H    \        R4       \        V P                  R4       R# )z3
Actualiza la tabla CM_Score con los datos finales
rS  N)r   r   r'  r   s   &r   rA  (ScoreProcessor.actualizar_tabla_cm_score  s    
 	z" 	D//<r   c                   \        V P                  4      p\        P                  ! 4       P	                  R4      pRV P
                  R,          '       d   RMR RV P
                  R,          '       d   RMR R	V P
                  R
,           RV P
                  R,           RV P
                  R,           RV P
                  R,           RV P
                  R,           RV P
                  R,           RV RV R2pRRRRR
V P
                  R
,          RV P
                  R,          RV P
                  R,          RV P
                  R,          RV P
                  R,          RV P
                  R,          RV P
                  R,          RVRVRV/# )u]   
Genera la respuesta JSON de éxito con estadísticas

Returns:
    dict: Respuesta completa
z%Y-%m-%d %H:%M:%SuZ   
=== RESUMEN DE ACTUALIZACIÓN ===

✅ Validación exitosa
✅ Respaldo guardado en FTP: r  u   Síu   No (sin conexión)ut   
✅ Datos del ERP obtenidos
✅ Mezcla de datos completada
✅ Tabla CM_Score actualizada
# 'OneDrive actualizado: r  Nou,   '


📊 Estadísticas:
- Filas procesadas: r  z
- Filas del ERP: r  z
- Filas del Excel: r  z
- Filas eliminadas: r  u.   

🗄️  Respaldo:
- Versión ScoreHistory: r  z

- Tabla: r  u   

⏱️  Tiempo: u   
📅 Fecha: r   r,  Trc  zScore actualizado correctamentetiempo_ejecucion	timestampresumen)calcular_tiempo_ejecucionr#  r   r"  strftimer)  )r   r  r  r  s   &   r   rC  (ScoreProcessor.generar_respuesta_exitosa	  s    5T5G5GHLLN++,?@	 )-

>(B(BuH\] ^ $(::.D#E#E%4P Q ZZ 234 5**[)* +JJ}-. /ZZ 234 5 **%678 9

**_
%	& '!" #K -4 t8

+= >K04::m4

+= >tzz*;<TZZ8DJJ~6 0w
 	
r   )
r(  r  r   r&  r%  r'  r!  r$  r)  r#  N)r   r   r   r   r   r   rI  rB  rM  r8  r9  r:  r<  r=  r>  r?  r@  rA  rC  r   r   r   s   @r   r  r    s^     
8L^G> &	'CJ<,4\	d:Xx0=0
 0
r   r  c                 V    \         P                  ! 4       P                  R4      p RV  R2# )za
Genera nombre con formato: Score_YYYY-MM-DD_HH-MM-SS.xlsx

Returns:
    str: Nombre del archivo
z%Y-%m-%d_%H-%M-%SScore_z.xlsx)r   r"  r  )r  s    r   rg  rg  ?  s*     ''(;<II;e$$r   c                  a ^ RI o^ RIHp \        R4       V P                  SP                  ! V 4      R4      p V P
                   FI  pW,          P                  R8X  g   K  \        RV 24       V3R lpW,          P                  V4      W&   KK  	  V P
                   F6  pW,          P                  R8X  g   K  W,          P                  R 4      W&   K8  	  \        R	4       V # )
u   
Limpia DataFrame antes de insertar en SQL

✅ VERSIÓN CORREGIDA: Maneja correctamente fechas 1900-01-01

Args:
    df (pd.DataFrame): DataFrame a limpiar
    
Returns:
    pd.DataFrame: DataFrame limpio
Nr   z  Limpiando datos para SQL...zdatetime64[ns]z     Limpiando columna de fecha: c                  < V e   SP                   ! V 4      '       d   R# \        V SP                  4      '       dH   V P                  4       pVP                  R8X  d%   VP
                  ^8X  d   VP                  ^8X  d   R# V# \        V \        4      '       d(   V P                  4       R8X  d   R# RV 9   g   RV 9   d   R# V # )u"   Convierte fechas inválidas a NoneNil  r  z
1900-01-01z
1900/01/01)	rz  
isinstance	Timestampto_pydatetimeyearmonthdayr   ru  )xfecharW  s   & r   limpiar_fecha1limpiar_dataframe_para_sql.<locals>.limpiar_fechac  s     9

 a..OO-E zzT)ekkQ.>599PQ># !L a%%wwyB# $q(LA,=# r   objectc                     V e,   \        V \        4      '       d   V P                  4       R8X  d   R # \        V \        4      '       d   V P                  4       # T # )Nr  )r  r   ru  )r  s   &r   <lambda>,limpiar_dataframe_para_sql.<locals>.<lambda>  sU    19As1C1C	UW$  Hmwxy{~mm`a`g`g`i  H  FG  Hr   u     ✓ Datos limpiados)pandasr   r   wherer|  ro  dtypeapply)r\  r   r  r  rW  s   &   @r   r~  r~  J  s     !	
)* 
"((2,	%B zz7==,,4SE:;> gmmM2BGG L zz7==H$gmm HBG  

!"Ir   c                Z    \        \        V 4      4      p\        \        V4      4      pW#8H  # )z
Compara dos listas de columnas

Args:
    cols1 (list): Primera lista
    cols2 (list): Segunda lista
    
Returns:
    bool: True si son iguales
)rx  ry  )cols1cols2set1set2s   &&  r   r  r    s(     ve}Dve}D<r   c                    \         P                  ! 4       pW,
          pVP                  4       p\        V^<,          4      p\        V^<,          4      pV RV R2# )z
Calcula tiempo transcurrido

Args:
    tiempo_inicio (datetime): Tiempo de inicio
    
Returns:
    str: Tiempo formateado (ej: "2m 34s")
zm s)r   r"  total_secondsint)r#  
tiempo_findeltasegundosminutossegss   &     r   r  r    sT     J&E""$H(b.!Gx"}DYba  r   c                    V P                  RR.R7      R 4       pV P                  RR.R7      R 4       pV P                  R4      R	 4       pV P                  R
4      R 4       pR# )u~   
Registra las rutas de actualización del Score en Flask

Args:
    app: Instancia de Flask
    mail: Instancia de Flask-Mail
z%/SyC/IngenieroControl/ActualizarScoreGET)methodsc                     \        R4      # )zP
Renderiza la vista HTML principal

Returns:
    render_template: Template HTML
z/SupYCtrol/IngenieroControl/ActualizarScore.html)r    r   r   vista_actualizar_score<ejecutar_actualizacion_score.<locals>.vista_actualizar_score  s     PQQr   z./SyC/IngenieroControl/ActualizarScore/ejecutarPOSTc            
          \        4       p V P                  4       pVR,          '       d   \        V4      ^3# \        V4      R3#   \         d(   p\        RRRR\	        T4       2/4      R3u Rp?# Rp?ii ; i)um   
Ejecuta el proceso completo de actualización del Score

Returns:
    jsonify: Respuesta JSON con resultado
r,    Fr-  u   Error crítico: rb  N)r  rI  r   r   r   )	processorrd  r   s      r   ejecutar_actualizacion<ejecutar_actualizacion_score.<locals>.ejecutar_actualizacion  s    	&(I "AACI##y)3..y)3.. 	5+CF84   	s"   5A A A7A2,A72A7z/onedrive/loginc                    ^ RI p \        R,          P                  \        R,          R7      pV P                  \        R,          \        R,          VR7      pVP	                  \        R,          \        R	,          R
7      p\        V4      # )u    Inicia el flujo de autorizaciónNr*   r   r   r    r"   r   r%   r$   )r%   r$   )r   r   r   r   get_authorization_request_urlr   )r   r*   app_msalauth_urls       r   onedrive_login4ejecutar_actualizacion_score.<locals>.onedrive_login  s     	#K077%k2 8 
	 55%k2-o> 6 
 99"8,(8 : 

 !!r   z	/callbackc                 6   ^ RI p ^ RIp\        P                  P	                  R4      pV'       g   R# \
        R,          P                  \
        R,          R7      pV P                  \
        R,          \
        R,          VR7      pVP                  V\
        R	,          \
        R
,          R7      pRV9   d<   \        \
        R,          R4      ;_uu_ 4       pVP                  WV4       RRR4       R# RVP	                  RV4       2R3#   + '       g   i     R# ; i)u/   Callback que recibe el código de autorizaciónNcoder*   r   r   r    r"   r   r%   r$   )r  r%   r$   r   r,   r   u  
            <html>
                <body style="font-family: Arial; padding: 50px; text-align: center;">
                    <h1 style="color: green;">✅ Autorización Exitosa</h1>
                    <p>OneDrive ha sido autorizado correctamente.</p>
                    <p>Los tokens se han guardado y se renovarán automáticamente.</p>
                    <p>Puedes cerrar esta ventana.</p>
                </body>
            </html>
            zError al obtener tokens: error_descriptionrb  )u.   Error: No se recibió código de autorizaciónr  )r   r   r   argsr  r   r   r   #acquire_token_by_authorization_coder   r   )r   r   r  r*   r  r   r   s          r   onedrive_callback7ejecutar_actualizacion_score.<locals>.onedrive_callback  s    	||'HH#K077%k2 8 
	 55%k2-o> 6 
 =="8,(8 > 
 V#o&893??1		&$ @	 /vzz:Mv/V.WXZ]]] @?	s   DD	N)route)appmailr  r  r  r  s   &&    r   ejecutar_actualizacion_scorer    s     	YY6YHR IR 	YY?&YR S2 	YY !" "", 	YY{*^ *^r   )FechaVentasFechaSimulacionesrq  AvanceDeSurtimientoAvaceDeEmisiones
Faltante_MAlternativa_MEn_PO_Altern_MNoPO_Alern_M	Estatus_MFecha_Llegada_M
Faltante_GAlternativa_GEn_PO_Altern_GNoPO_Alern_G	Estatus_GFecha_Llegada_G)(r   r   dotenvr   r  rW  r   r   flaskr   r   r   r   Consultas_SQL.conexionr	   r
   7Consultas_SQL.SupYCtrol.IngDeControl.ActualizarScoreSQLr   r   r   r   r   r   r   r   r   2App.SupyCtrol_Module.IngenieroControl.ValidarScorer   getenvr   r   rw  r   r   r  rg  r~  r  r  r  r  r   r   <module>r     s   
     = = D
 
 
 `
  BIIj!
BIIj!		*%l	
 u ;'/0RYY78 4 
 "))/0 ? @3 37D  "$.068DFQ*,57=?L 	 # %0 2> @O 		 $	 &5	 7B	 DM	
 	
 *
 ,:
 <K 	 / 1A CN 	 ' )8 :D FS 	 % '0 2: <M 	 ' )8 :G IX 	 " $1 3@ BK MU 	 - /: <K MZ 	 $ &3 5B DO 	 ' )8 :D FS 	 ( *2 4A   		#	%*	,6	8R		!3	5L	Na	 		 *	 ,C	 E^	 			 $		 &:		 <R		
 		
  2	
 4?	
 AL	
 Na	 		 %	 '.	 0;	 =I	 KQ	 		 -	 /:	 <C	 EP	 		 	 2	  7! NQ QpEf EfV\	
 \	
D%CL$!0o^r   