# ==============================================================================
  #                 ACTUALIZACIÓN DE LIQUIDEZ - CORREGIDO
  #                          31/12/2025
  # ==============================================================================
  #                          CARGA DE LIBRERIAS
  # ==============================================================================
  rm(list = ls())

  paquetes <- c("mailR","odbc", "dplyr","glue", "base64enc","httr",
                "jsonlite","scales", "openxlsx", "lubridate")
  
  for (pkg in paquetes) {
    if (!requireNamespace(pkg, quietly = TRUE)) {
      message("Instalando paquete: ", pkg)
      tryCatch({
        install.packages(pkg, dependencies = TRUE, repos = "https://cloud.r-project.org")
      }, error = function(e) {
        message("❌ Error instalando ", pkg, ": ", e$message)
      })
    }
    library(pkg, character.only = TRUE)
  }
  
  # ==============================================================================
  #                CAPTURA DE ARGUMENTOS (MODO + MES + AÑO)
  # ==============================================================================
  
  args <- commandArgs(trailingOnly = TRUE)
  
  meses_esp <- c("Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", 
                 "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre")
  
  # -----------------------------
  # 1️⃣ MODO con_pre / sin_pre
  # -----------------------------
  modo_input <- ifelse(length(args) >= 1, tolower(trimws(args[1])), "sin_pre")
  
  if (!modo_input %in% c("con_pre", "sin_pre")) {
    stop("❌ Primer argumento inválido. Use 'con_pre' o 'sin_pre'")
  }
  
  incluir_preregistro <- (modo_input == "con_pre")
  sufijo_pre <- modo_input
  
  message("✅ MODO ACTIVO:", sufijo_pre)
  
  # -----------------------------
  # 2️⃣ MES (opcional)
  # -----------------------------
  if (length(args) >= 2) {
    
    mes_input <- trimws(args[2])
    
    if (grepl("^[0-9]+$", mes_input)) {
      
      num_mes <- as.numeric(mes_input)
      
      if (num_mes < 1 || num_mes > 12) {
        stop("❌ Número de mes inválido (1-12).")
      }
      
    } else {
      
      mes_input_cap <- tools::toTitleCase(tolower(mes_input))
      
      if (!mes_input_cap %in% meses_esp) {
        stop("❌ Nombre de mes inválido.")
      }
      
      num_mes <- match(mes_input_cap, meses_esp)
    }
    
  } else {
    
    num_mes <- as.numeric(format(Sys.Date(), "%m"))
  }
  
  mes_actual <- meses_esp[num_mes]
  
  # -----------------------------
  # 3️⃣ AÑO (opcional)
  # -----------------------------
  if (length(args) >= 3) {
    
    anio_input <- as.numeric(args[3])
    
    if (is.na(anio_input) || anio_input < 2020 || anio_input > 2100) {
      stop("❌ Año inválido.")
    }
    
    anio_actual <- anio_input
    
  } else {
    
    anio_actual <- as.numeric(format(Sys.Date(), "%Y"))
  }
  
  message("📅 PERIODO SELECCIONADO:", mes_actual, anio_actual)
  
# ==============================================================================
#                          OBTENCIÓN DE DATOS API
# ==============================================================================
  
  url <- "https://apipagotrack.mexiclientes.com/index.php?action=estimacionLiquidez"
  url_publica_base <- "https://rsie.mexiclientes.com/r_data/Reportes/Liquidez/"
  estatus_base <- c("Creado", "Turnado", "RegistradoSAP", "Remesa", 
                    "RemesaAprobada", "Observaciones", "Devuelto", "Rechazado")
  
  estatus_final <- if (incluir_preregistro) {
    message("ℹ️ REPORTE TIPO: COMPLETO (Con PreRegistro)")
    c("PreRegistro", estatus_base)
  } else {
    message("ℹ️ REPORTE TIPO: ESTÁNDAR (Sin PreRegistro)")
    estatus_base
  }
  
  datos <- list(
    mes = mes_actual,
    anio = anio_actual,
    estatus = estatus_final,
    tipoTramite = c("OP", "OPO", "SRF")
  )
  
  message("📡 Conectando a la API para:", mes_actual, anio_actual)
  
  respuesta <- tryCatch({
    POST(
      url,
      body = toJSON(datos, auto_unbox = TRUE),
      encode = "json",
      content_type_json(),
      timeout(60)
    )
  }, error = function(e) {
    stop("❌ Error en conexión con API: ", e$message)
  })
  
  codigo_estatus <- status_code(respuesta)
  contenido_texto <- content(respuesta, "text", encoding = "UTF-8")
  
  if (codigo_estatus == 200) {
    
    message("✅ API respondió correctamente (200). Procesando datos...")
    
    json_respuesta <- fromJSON(contenido_texto)
    
    if (!is.null(json_respuesta$data) && length(json_respuesta$data) > 0) {
      
      df_completo <- cbind(
        json_respuesta$data[names(json_respuesta$data) != "Fondos"],
        json_respuesta$data$Fondos
      )
      
    } else {
      message("⚠️ La API respondió 200 pero no hay datos.")
      quit(save = "no", status = 0)
    }
    
  } else if (codigo_estatus == 404 &&
             grepl("No se encontraron estimación de liquidez", contenido_texto)) {
    
    message("⚠️ No hay datos disponibles para:", mes_actual, anio_actual)
    quit(save = "no", status = 0)
    
  } else {
    
    stop("❌ Error API. Código:", codigo_estatus,
         " | Respuesta:", substr(contenido_texto, 1, 200))
  }
# ==============================================================================
# PROCESAMIENTO DE DATOS
# ==============================================================================
  options(scipen = 999) 
  
  # Copia completa antes de reducir columnas (para vista "Detalle completo")
  df_full <- df_completo
  
  df_completo <- df_completo %>%
    select(ID_CONTRATO, Secretaria, Proveedor, Concepto, Importe, F10050)%>%
    mutate(
      Importe = as.numeric(Importe),
      F10050  = as.numeric(F10050)
    )
  
  df_filtrado <- df_completo %>%
    filter(F10050 >= 1000000) %>%
    mutate(Número = row_number()) %>%
    select(Número, everything()) %>%
    arrange(desc(F10050))
  
  total_importe <- sum(df_completo$Importe, na.rm = TRUE)
  total_f10050  <- sum(df_completo$F10050,  na.rm = TRUE)
  # ==========================================================
  #                  RESUMEN DE FONDOS (numérico)
  # ==========================================================
  df_solo_fondos <- json_respuesta$data$Fondos
  totales_vector <- colSums(df_solo_fondos, na.rm = TRUE)
  
  resumen_fondos <- data.frame(
    FONDO   = names(totales_vector),
    IMPORTE = as.numeric(totales_vector),
    stringsAsFactors = FALSE
  ) %>% arrange(desc(IMPORTE))
  
  gran_total_valor <- sum(resumen_fondos$IMPORTE, na.rm = TRUE)
  
  resumen_fondos <- bind_rows(
    resumen_fondos,
    data.frame(FONDO = "TOTAL", IMPORTE = gran_total_valor, stringsAsFactors = FALSE)
  )
  
  # ==========================================================
  #              DETALLE COMPLETO DE LIQUIDEZ
  # ==========================================================
  cols_detalle <- c("ID_CONTRATO","Secretaria","Proveedor","Concepto","Estatus",
                    "Mes","TipoTramite","Analista","Importe",
                    "F10050","F10125","F10126","F10329")
  cols_existentes <- intersect(cols_detalle, names(df_full))
  
  df_detalle <- df_full %>%
    select(all_of(cols_existentes)) %>%
    mutate(across(any_of(c("Importe","F10050","F10125","F10126","F10329")),
                  ~ as.numeric(.))) %>%
    arrange(desc(Importe)) %>%
    mutate(Número = row_number()) %>%
    select(Número, everything())
  
# ==============================================================================
#                       GENERACIÓN DE XLSX (3 PESTAÑAS)
# ==============================================================================
  ruta_logo_puebla <- "/var/www/html/rsie/r_imagenes/logos/Logo.png"
  ruta_logo_teso   <- "/var/www/html/rsie/r_imagenes/logos/teso.png"
  
  wb <- createWorkbook()
  
  # ---- Estilos institucionales ----
  estilo_titulo <- createStyle(fontSize = 14, textDecoration = "bold",
                               fontColour = "#671C33", halign = "center")
  estilo_meta   <- createStyle(fontSize = 10, textDecoration = "bold")
  estilo_header <- createStyle(fontColour = "#FFFFFF", fgFill = "#671C33",
                               halign = "center", valign = "center",
                               textDecoration = "bold",
                               border = "TopBottomLeftRight",
                               borderColour = "#000000", wrapText = TRUE)
  estilo_total      <- createStyle(fontColour = "#FFFFFF", fgFill = "#671C33",
                                   textDecoration = "bold", halign = "right",
                                   border = "TopBottomLeftRight")
  estilo_total_num  <- createStyle(fontColour = "#FFFFFF", fgFill = "#671C33",
                                   textDecoration = "bold", halign = "right",
                                   border = "TopBottomLeftRight",
                                   numFmt = "\"$\"#,##0.00")
  estilo_money  <- createStyle(numFmt = "\"$\"#,##0.00", halign = "right",
                               border = "TopBottomLeftRight")
  estilo_text   <- createStyle(halign = "left", valign = "top", wrapText = TRUE,
                               border = "TopBottomLeftRight")
  estilo_center <- createStyle(halign = "center", valign = "center",
                               border = "TopBottomLeftRight")
  
  # ---- Helpers ----
  agregar_logos <- function(sheet, col_logo_der = 7) {
    if (file.exists(ruta_logo_teso)) {
      tryCatch(
        insertImage(wb, sheet, ruta_logo_teso,
                    startRow = 1, startCol = 1, width = 2.5, height = 0.7),
        error = function(e) message("⚠️ logo teso omitido: ", e$message)
      )
    }
    if (file.exists(ruta_logo_puebla)) {
      tryCatch(
        insertImage(wb, sheet, ruta_logo_puebla,
                    startRow = 1, startCol = col_logo_der, width = 2, height = 0.7),
        error = function(e) message("⚠️ logo puebla omitido: ", e$message)
      )
    }
  }
  
  escribir_cabecera <- function(sheet, n_cols) {
    writeData(wb, sheet,
              x = paste0("Reporte de liquidez: ", format(Sys.time(), "%d/%m/%Y %H:%M")),
              startRow = 5, startCol = 1)
    writeData(wb, sheet,
              x = paste0("Corresponde al mes de: ", mes_actual, " ", anio_actual),
              startRow = 6, startCol = 1)
    addStyle(wb, sheet, estilo_meta, rows = 5:6, cols = 1, gridExpand = TRUE)
  }
  
  # ======================================================
  #          HOJA 1: RESUMEN POR FONDO
  # ======================================================
  hoja1 <- "Resumen por Fondo"
  addWorksheet(wb, hoja1, gridLines = FALSE)
  agregar_logos(hoja1, col_logo_der = 5)
  escribir_cabecera(hoja1)
  
  writeData(wb, hoja1, x = "RESUMEN POR FONDO", startRow = 8, startCol = 1)
  mergeCells(wb, hoja1, rows = 8, cols = 1:2)
  addStyle(wb, hoja1, estilo_titulo, rows = 8, cols = 1)
  
  writeData(wb, hoja1, resumen_fondos,
            startRow = 9, startCol = 1, headerStyle = estilo_header)
  
  n_resumen <- nrow(resumen_fondos)
  if (n_resumen > 1) {
    rangos_r <- 10:(9 + n_resumen - 1)   # filas de detalle (sin la fila TOTAL)
    addStyle(wb, hoja1, estilo_center, rows = rangos_r, cols = 1, gridExpand = TRUE)
    addStyle(wb, hoja1, estilo_money,  rows = rangos_r, cols = 2, gridExpand = TRUE)
  }
  fila_total_r <- 9 + n_resumen
  addStyle(wb, hoja1, estilo_total,     rows = fila_total_r, cols = 1)
  addStyle(wb, hoja1, estilo_total_num, rows = fila_total_r, cols = 2)
  
  setColWidths(wb, hoja1, cols = 1:2, widths = c(20, 28))
  setRowHeights(wb, hoja1, rows = 9, heights = 28)
  
  # ======================================================
  #          HOJA 2: PENDIENTES F10050
  # ======================================================
  hoja2 <- "Pendientes F10050"
  addWorksheet(wb, hoja2, gridLines = FALSE)
  agregar_logos(hoja2, col_logo_der = 7)
  escribir_cabecera(hoja2)
  
  writeData(wb, hoja2,
            x = "PRINCIPALES PENDIENTES DE PAGO CON RECURSOS PROPIOS 10050",
            startRow = 8, startCol = 1)
  mergeCells(wb, hoja2, rows = 8, cols = 1:7)
  addStyle(wb, hoja2, estilo_titulo, rows = 8, cols = 1)
  
  writeData(wb, hoja2, df_filtrado,
            startRow = 9, startCol = 1, headerStyle = estilo_header)
  
  n_pend <- nrow(df_filtrado)
  # if (n_pend > 0) {
  #   rangos_p <- 10:(9 + n_pend)
  #   addStyle(wb, hoja2, estilo_center, rows = rangos_p, cols = 1:2, gridExpand = TRUE)
  #   addStyle(wb, hoja2, estilo_text,   rows = rangos_p, cols = 3:5, gridExpand = TRUE)
  #   addStyle(wb, hoja2, estilo_money,  rows = rangos_p, cols = 6:7, gridExpand = TRUE)
    
  #   fila_t2 <- 10 + n_pend
  #   writeData(wb, hoja2, x = "TOTALES", startRow = fila_t2, startCol = 1)
  #   mergeCells(wb, hoja2, rows = fila_t2, cols = 1:5)
  #   writeData(wb, hoja2, x = total_importe, startRow = fila_t2, startCol = 6)
  #   writeData(wb, hoja2, x = total_f10050,  startRow = fila_t2, startCol = 7)
  #   addStyle(wb, hoja2, estilo_total,     rows = fila_t2, cols = 1:5, gridExpand = TRUE)
  #   addStyle(wb, hoja2, estilo_total_num, rows = fila_t2, cols = 6:7, gridExpand = TRUE)
  # }
  if (n_pend > 0) {
    rangos_p <- 10:(9 + n_pend)
    addStyle(wb, hoja2, estilo_center, rows = rangos_p, cols = 1:2, gridExpand = TRUE)
    addStyle(wb, hoja2, estilo_text,   rows = rangos_p, cols = 3:5, gridExpand = TRUE)
    addStyle(wb, hoja2, estilo_money,  rows = rangos_p, cols = 6:7, gridExpand = TRUE)
    
    # ---- Totales de los TOP (solo df_filtrado) ----
    total_importe_top <- sum(df_filtrado$Importe, na.rm = TRUE)
    total_f10050_top  <- sum(df_filtrado$F10050,  na.rm = TRUE)
    otros_f10050      <- total_f10050 - total_f10050_top
    
    fila_t2 <- 10 + n_pend
    writeData(wb, hoja2, x = "TOTALES", startRow = fila_t2, startCol = 1)
    mergeCells(wb, hoja2, rows = fila_t2, cols = 1:5)
    writeData(wb, hoja2, x = total_importe_top, startRow = fila_t2, startCol = 6)
    writeData(wb, hoja2, x = total_f10050_top,  startRow = fila_t2, startCol = 7)
    addStyle(wb, hoja2, estilo_total,     rows = fila_t2, cols = 1:5, gridExpand = TRUE)
    addStyle(wb, hoja2, estilo_total_num, rows = fila_t2, cols = 6:7, gridExpand = TRUE)
    
    # ---- OTROS 10050 / TOTAL 10050 ----
    estilo_lbl_res <- createStyle(textDecoration = "bold", halign = "left")
    estilo_num_res <- createStyle(textDecoration = "bold", halign = "right",
                                  numFmt = "\"$\"#,##0.00")
    
    fila_otros <- fila_t2 + 1
    fila_tot10 <- fila_t2 + 2
    
    writeData(wb, hoja2, x = "OTROS 10050", startRow = fila_otros, startCol = 6)
    writeData(wb, hoja2, x = otros_f10050,  startRow = fila_otros, startCol = 7)
    
    writeData(wb, hoja2, x = "TOTAL 10050", startRow = fila_tot10, startCol = 6)
    writeData(wb, hoja2, x = total_f10050,  startRow = fila_tot10, startCol = 7)
    
    addStyle(wb, hoja2, estilo_lbl_res, rows = c(fila_otros, fila_tot10), cols = 6, gridExpand = TRUE)
    addStyle(wb, hoja2, estilo_num_res, rows = c(fila_otros, fila_tot10), cols = 7, gridExpand = TRUE)
  }
  
  setColWidths(wb, hoja2, cols = 1:7, widths = c(8, 12, 28, 28, 60, 18, 18))
  setRowHeights(wb, hoja2, rows = 9, heights = 30)
  freezePane(wb, hoja2, firstActiveRow = 10)
  
  # ======================================================
  #          HOJA 3: DETALLE COMPLETO
  # ======================================================
  hoja3 <- "Detalle Completo"
  addWorksheet(wb, hoja3, gridLines = FALSE)
  n_cols_det <- ncol(df_detalle)
  agregar_logos(hoja3, col_logo_der = max(7, n_cols_det - 1))
  escribir_cabecera(hoja3)
  
  writeData(wb, hoja3, x = "DETALLE COMPLETO DE LIQUIDEZ",
            startRow = 8, startCol = 1)
  mergeCells(wb, hoja3, rows = 8, cols = 1:n_cols_det)
  addStyle(wb, hoja3, estilo_titulo, rows = 8, cols = 1)
  
  writeData(wb, hoja3, df_detalle,
            startRow = 9, startCol = 1, headerStyle = estilo_header)
  
  n_det <- nrow(df_detalle)
  cols_num_det    <- which(names(df_detalle) %in% c("Importe","F10050","F10125","F10126","F10329"))
  cols_text_det   <- which(names(df_detalle) %in% c("Secretaria","Proveedor","Concepto","Estatus","Mes","TipoTramite","Analista"))
  cols_center_det <- which(names(df_detalle) %in% c("Número","ID_CONTRATO"))
  
  if (n_det > 0) {
    rangos_d <- 10:(9 + n_det)
    if (length(cols_center_det) > 0)
      addStyle(wb, hoja3, estilo_center, rows = rangos_d, cols = cols_center_det, gridExpand = TRUE)
    if (length(cols_text_det) > 0)
      addStyle(wb, hoja3, estilo_text,   rows = rangos_d, cols = cols_text_det, gridExpand = TRUE)
    if (length(cols_num_det) > 0)
      addStyle(wb, hoja3, estilo_money,  rows = rangos_d, cols = cols_num_det, gridExpand = TRUE)
    
    fila_t3   <- 10 + n_det
    col_imp   <- which(names(df_detalle) == "Importe")
    
    writeData(wb, hoja3, x = "TOTALES", startRow = fila_t3, startCol = 1)
    if (length(col_imp) > 0 && col_imp > 1) {
      mergeCells(wb, hoja3, rows = fila_t3, cols = 1:(col_imp - 1))
      addStyle(wb, hoja3, estilo_total, rows = fila_t3,
               cols = 1:(col_imp - 1), gridExpand = TRUE)
    }
    
    for (col_name in c("Importe","F10050","F10125","F10126","F10329")) {
      col_idx <- which(names(df_detalle) == col_name)
      if (length(col_idx) > 0) {
        writeData(wb, hoja3, x = sum(df_detalle[[col_name]], na.rm = TRUE),
                  startRow = fila_t3, startCol = col_idx)
        addStyle(wb, hoja3, estilo_total_num, rows = fila_t3,
                 cols = col_idx, gridExpand = TRUE)
      }
    }
  }
  
  anchos_det <- sapply(names(df_detalle), function(n) {
    switch(n,
           "Número"      = 8,
           "ID_CONTRATO" = 12,
           "Secretaria"  = 28,
           "Proveedor"   = 28,
           "Concepto"    = 50,
           "Estatus"     = 16,
           "Mes"         = 12,
           "TipoTramite" = 14,
           "Analista"    = 24,
           "Importe"     = 18,
           "F10050"      = 18,
           "F10125"      = 14,
           "F10126"      = 18,
           "F10329"      = 18,
           15)
  })
  setColWidths(wb, hoja3, cols = seq_along(anchos_det), widths = anchos_det)
  setRowHeights(wb, hoja3, rows = 9, heights = 32)
  freezePane(wb, hoja3, firstActiveRow = 10)
  
# ==============================================================================
#                       PROCESO DE LOG Y ENVÍO
# ==============================================================================
  archivo_log_excel <- "/var/www/html/rsie/r_data/Log_Envios_Remesas_Global.xlsx"
  ruta_reportes     <- "/var/www/html/rsie/r_data/Reportes/Liquidez/"
  
  cargar_log_global <- function(ruta) {
    if (file.exists(ruta)) return(read.xlsx(ruta))
    return(data.frame(Identificador_Remesa = character(),
                      Ruta_Imagen          = character(),
                      Enviado_A            = character(),
                      Estado               = character(),
                      Tipo_Reporte         = character(),
                      Hora_Generacion      = as.POSIXct(character()),
                      Hora_Envio           = as.POSIXct(character()),
                      stringsAsFactors     = FALSE))
  }
  
  log_global <- cargar_log_global(archivo_log_excel)
  
  enviar_reporte_xlsx <- function(ruta_xlsx, Identificador_Remesa) {
    send.mail(from = "tesoreria.decp@ayuntamientopuebla.gob.mx",
              to   = "agustin.martinez@ayuntamientopuebla.gob.mx",
              subject = paste("Reporte Liquidez -", Identificador_Remesa),
              body = glue("<html><body><h2 style='color: #671c33;'>Reporte de Estimación de Liquidez</h2><p>Identificador: <b>{Identificador_Remesa}</b></p></body></html>"),
              html = TRUE,
              attach.files = c(ruta_xlsx),
              smtp = list(host.name = "smtp.office365.com",
                          port      = 587,
                          user.name = "tesoreria.decp@ayuntamientopuebla.gob.mx",
                          passwd    = "tesoP2025!!YOdta703",
                          tls       = TRUE),
              authenticate = TRUE, send = TRUE)
  }
  
  # 1. RECONCILIACIÓN DE PENDIENTES (.xlsx)
  archivos_en_carpeta <- list.files(ruta_reportes, pattern = "\\.xlsx$", full.names = TRUE)
  if (length(archivos_en_carpeta) > 0) {
    for (xlsx_path in archivos_en_carpeta) {
      id_extraido <- gsub("ReporteLiquidez-|\\.xlsx", "", basename(xlsx_path))
      ya_enviado  <- any(log_global$Identificador_Remesa == id_extraido &
                         log_global$Estado == "Enviado", na.rm = TRUE)
      
      if (!ya_enviado) {
        message("📩 Sincronizando pendiente: ", id_extraido)
        try({
          enviar_reporte_xlsx(xlsx_path, id_extraido)
          log_global <- log_global %>% filter(Identificador_Remesa != id_extraido)
          log_global <- rbind(log_global, data.frame(
            Identificador_Remesa = id_extraido,
            Ruta_Imagen          = paste0(url_publica_base, basename(xlsx_path)),
            Enviado_A            = "agustin.martinez@ayuntamientopuebla.gob.mx",
            Estado               = "Enviado",
            Tipo_Reporte         = paste0("Liquidez", sufijo_pre),
            Hora_Generacion      = format(file.info(xlsx_path)$mtime, "%d/%m/%Y %H:%M:%S"),
            Hora_Envio           = format(Sys.time(),                "%d/%m/%Y %H:%M:%S"),
            stringsAsFactors     = FALSE
          ))
        })
      }
    }
  }
  
  # 2. GENERACIÓN DE NUEVO REPORTE
  message("✨ Generando reporte XLSX nuevo...")
  timestamp_id    <- paste0(sufijo_pre, "-", format(Sys.time(), "%d-%m-%y-%H-%M"))
  nombre_xlsx     <- paste0("ReporteLiquidez-", timestamp_id, ".xlsx")
  ruta_final_xlsx <- file.path(ruta_reportes, nombre_xlsx)
  
  tryCatch({
    saveWorkbook(wb, ruta_final_xlsx, overwrite = TRUE)
    
    if (file.exists(ruta_final_xlsx)) {
      enviar_reporte_xlsx(ruta_final_xlsx, timestamp_id)
      log_global <- rbind(log_global, data.frame(
        Identificador_Remesa = timestamp_id,
        Ruta_Imagen          = paste0(url_publica_base, nombre_xlsx),
        Enviado_A            = "agustin.martinez@ayuntamientopuebla.gob.mx",
        Estado               = "Enviado",
        Tipo_Reporte         = paste0("Liquidez", sufijo_pre),
        Hora_Generacion      = format(Sys.time(), "%d/%m/%Y %H:%M:%S"),
        Hora_Envio           = format(Sys.time(), "%d/%m/%Y %H:%M:%S"),
        stringsAsFactors     = FALSE
      ))
    }
  }, error = function(e) message("❌ Error: ", e$message))
  
  write.xlsx(log_global, archivo_log_excel, overwrite = TRUE)