package main import ( "context" "daemonService/internal/handlers" "daemonService/internal/models" svc "daemonService/internal/models" "daemonService/internal/models/obtencionCodigos" "daemonService/internal/repositories" "daemonService/internal/services/obtencionCodigo" "daemonService/internal/services/procesar" "daemonService/internal/services/sincronizacionDatos" "flag" "fmt" "github.com/robfig/cron/v3" "log" "net/http" "os" "os/signal" "syscall" "time" "daemonService/internal/api" "daemonService/internal/config" "daemonService/internal/database" "daemonService/internal/notifications" "daemonService/internal/services" ) func main() { configFile := flag.String("config", "configs/config.yaml", "Ruta al archivo de configuración") runOnce := flag.Bool("run-once", false, "Ejecutar servicios una vez y salir") serviceName := flag.String("service", "", "Nombre del servicio específico a ejecutar") flag.Parse() // Cargar configuración cfg, err := config.LoadConfig(*configFile) if err != nil { log.Fatalf("Error al cargar configuración: %v", err) } // Inicializar la base de datos db, err := database.InitDB(*cfg) if err != nil { log.Fatalf("Error al inicializar la base de datos: %v", err) } defer db.Close() // Crear sistema de notificaciones notifier := notifications.CreateNotificationSender(cfg.Notifications.Enabled, cfg.Notifications.Method, cfg.Notifications.Config) if notifier != nil { log.Printf("Sistema de notificaciones iniciado: %s", cfg.Notifications.Method) } // Crear servicios a partir de la configuración var servicesList []models.SoapService var servicesList2 []obtencionCodigos.CuisService for _, svcCfg := range cfg.Services { if !svcCfg.Enabled { continue } // Crear el logger para el servicio serviceLogger := log.New(log.Writer(), fmt.Sprintf("[%s] ", svcCfg.Name), log.LstdFlags) baseSvc := svc.ServiceModel{ Name: svcCfg.Name, Enabled: svcCfg.Enabled, Concurrency: svcCfg.Concurrency, DB: db, SOAPEndpoint: cfg.SOAP.FACTURA_SINCRONIZACION.Endpoint, SOAPTimeout: time.Duration(cfg.SOAP.FACTURA_SINCRONIZACION.Timeout) * time.Second, SOAPRetries: cfg.SOAP.FACTURA_SINCRONIZACION.Retries, NotificationSender: notifier, KeyToken: "", ValueToken: "", TagNames: "", QueryInsert: "", MsgCustomResponse: "", } cuisServiceModel := obtencionCodigos.CuisServiceModel{ Name: svcCfg.Name, Enabled: svcCfg.Enabled, Concurrency: svcCfg.Concurrency, DB: db, SOAPEndpoint: cfg.SOAP.FACTURA_CODIGO.Endpoint, SOAPTimeout: time.Duration(cfg.SOAP.FACTURA_CODIGO.Timeout) * time.Second, SOAPRetries: cfg.SOAP.FACTURA_CODIGO.Retries, NotificationSender: notifier, TokenKey: "", TokenValue: "", TagNames: "", TagNamesCufd: "", QueryInsert: "", MsgCustomResponse: "", } //registrar-empresa solo para registrar empresa cuisServiceModel.TagNames = "cuis" cuisServiceModel.TagNamesCufd = "cufd" cuisServiceModel.QueryInsert = "INSERT INTO registroEmpresa (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" cuisServiceModel.MsgCustomResponse = "Registrar Empresa" procesarRegistro := procesar.NewProcesarRegistro(cuisServiceModel) empresaRepository := repositories.NewEmpresaRepository(cuisServiceModel) cuisService := services.NewCuisService(cuisServiceModel, serviceLogger) cuisRepository := repositories.NewCuisRepository(baseSvc, cuisService) cronHandler := handlers.NewCronHandler(cuisService, empresaRepository, cuisRepository, procesarRegistro) apiHandler := handlers.NewApiHandler(cuisService, cuisRepository, procesarRegistro) // Llamar a NewObtencionCodigoService correctamente obtencionCodigoSvc := obtencionCodigo.NewObtencionCodigoService( cuisServiceModel, cuisService, cronHandler, apiHandler, ) switch svcCfg.Name { case "registrar-empresa": servicesList2 = append(servicesList2, obtencionCodigoSvc) break case "actividades": //todo revisar que pasa baseSvc.TagNames = "sincronizarActividades" baseSvc.QueryInsert = "INSERT INTO sincronizar_actividades (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado de Actividades" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "sincronizar_fecha_hora": //todo revisar que pasa baseSvc.TagNames = "sincronizarFechaHora" baseSvc.QueryInsert = "INSERT INTO sincronizar_fecha_hora (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Sincronizar la hora" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "sincronizar_mensaje_servicio": //todo revisar que pasa baseSvc.TagNames = "sincronizarListaMensajesServicios" baseSvc.QueryInsert = "INSERT INTO sincronizar_mensaje_servicio (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Sincronizar mensaje servicio" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "leyendas_factura": baseSvc.TagNames = "sincronizarListaLeyendasFactura" baseSvc.QueryInsert = "INSERT INTO sincronizar_leyendas_factura (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado Leyendas de Facturas" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "producto-servicio": baseSvc.TagNames = "sincronizarListaProductosServicios" baseSvc.QueryInsert = "INSERT INTO sincronizar_productos_servicios (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado Producto Servicio" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_motivo_anulacion": baseSvc.TagNames = "sincronizarParametricaMotivoAnulacion" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_motivo_anulacion (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado motivo anulacion" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_documento_identidad": baseSvc.TagNames = "sincronizarParametricaTipoDocumentoIdentidad" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_documento_identidad (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de documento de identidad" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_documento_sector": baseSvc.TagNames = "sincronizarParametricaTipoDocumentoSector" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_documento_sector (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de documento sector" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_emision": //todo falta este baseSvc.TagNames = "sincronizarParametricaTipoEmision" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_emision (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de emision" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_actividades_documento_sector": //todo falta este baseSvc.TagNames = "sincronizarListaActividadesDocumentoSector" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_actividades_documento_sector (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de emision" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_metodo_pago": baseSvc.TagNames = "sincronizarParametricaTipoMetodoPago" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_metodo_pago (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos metodo de pago" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_moneda": baseSvc.TagNames = "sincronizarParametricaTipoMoneda" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_moneda (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de moneda" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_punto_venta": baseSvc.TagNames = "sincronizarParametricaTipoPuntoVenta" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_punto_venta (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de punto de venta" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_factura": //todo este falta baseSvc.TagNames = "sincronizarParametricaTiposFactura" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_factura (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de factura" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_unidad_medida": baseSvc.TagNames = "sincronizarParametricaUnidadMedida" baseSvc.QueryInsert = "INSERT INTO sincronizar_tipo_unidad_medida (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado tipos de unidad de medida" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "tipo_evento_significativos": baseSvc.TagNames = "sincronizarParametricaEventosSignificativos" baseSvc.QueryInsert = "INSERT INTO sincronizar_evento_significativo (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado eventos significativos" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "sincronizar_tipo_habitacion": baseSvc.TagNames = "sincronizarParametricaTipoHabitacion" baseSvc.QueryInsert = "INSERT INTO sincronizar_Tipo_Habitacion (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado eventos significativos" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break case "sincronizar_pais_origen": baseSvc.TagNames = "sincronizarParametricaPaisOrigen" baseSvc.QueryInsert = "INSERT INTO sincronizar_Pais_Origen (codigo, reqSoap, respSoap, reqJson, respJson, cuis_id, fecha_creacion) VALUES ($1, $2, $3, $4, $5, $6, NOW() AT TIME ZONE 'America/La_Paz') ON CONFLICT (codigo, cuis_id) DO UPDATE SET reqSoap = EXCLUDED.reqSoap, respSoap = EXCLUDED.respSoap, reqJson = EXCLUDED.reqJson, respJson = EXCLUDED.respJson, cuis_id = EXCLUDED.cuis_id, fecha_actualizacion = NOW() AT TIME ZONE 'America/La_Paz';" baseSvc.MsgCustomResponse = "Listado eventos significativos" servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ ServiceModel: baseSvc, }) break default: log.Printf("Servicio desconocido: %s", svcCfg.Name) } } // Si se especifica un servicio específico, filtrar la lista. if *serviceName != "" { var filtered []models.SoapService for _, s := range servicesList { if s.GetName() == *serviceName { filtered = append(filtered, s) break } } if len(filtered) == 0 { log.Fatalf("No se encontró el servicio: %s", *serviceName) } servicesList = filtered log.Printf("Ejecutando solo el servicio: %s", *serviceName) } // Crear contexto con cancelación para manejar señales de terminación. ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Canal para capturar señales del sistema. sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // Iniciar servidor HTTP si se configura puerto para API. var httpServer *http.Server if cfg.API.Port > 0 { httpServer = api.StartAPIServer(cfg.API.Port, servicesList, servicesList2, false) } // Goroutine para manejar señales. go func() { sig := <-sigChan log.Printf("Recibida señal: %v. Cerrando...", sig) if notifier != nil { notifier.SendNotification("Servicio detenido", fmt.Sprintf("El servicio fue detenido por la señal: %v", sig)) } cancel() if httpServer != nil { log.Println("Cerrando servidor HTTP...") shutdownCtx, cancelServer := context.WithTimeout(context.Background(), 10*time.Second) defer cancelServer() if err := httpServer.Shutdown(shutdownCtx); err != nil { log.Printf("Error al cerrar servidor HTTP: %v", err) } } }() // Si se debe ejecutar solo una vez. if *runOnce { errors := services.RunAllServices(ctx, servicesList, false) if len(errors) > 0 { for _, e := range errors { log.Printf("Error: %v", e) } os.Exit(1) } return } // Configurar primer cron c := cron.New(cron.WithSeconds()) _, err = c.AddFunc(cfg.Schedule.FACTURA_SINCRONIZACION.Cron, func() { log.Println("Iniciando ejecución programada de servicios") execCtx, execCancel := context.WithCancel(ctx) defer execCancel() errors := services.RunAllServices(execCtx, servicesList, true) if len(errors) > 0 { errMsg := "Errores durante la ejecución programada:" for _, err := range errors { errMsg += "\n- " + err.Error() } if notifier != nil { notifier.SendNotification("Errores en ejecución programada", errMsg) } log.Println(errMsg) } else { log.Println("Ejecución programada completada exitosamente") } }) if err != nil { log.Fatalf("Error al programar tareas: %v", err) } // Configurar segundo cron cronGetCode := cron.New(cron.WithSeconds()) _, err = cronGetCode.AddFunc(cfg.Schedule.FACTURA_CODIGO.Cron, func() { log.Println("Iniciando ejecución programada para Obtener Codigos") execCtx, cancel := context.WithCancel(context.Background()) defer cancel() //execCtx, execCancel := context.WithCancel(ctx) //defer execCancel() errors := services.RunAllGetCodeServices(execCtx, servicesList2, true) if len(errors) > 0 { errMsg := "Errores durante la ejecución programada Obtener Codigos:" for _, err := range errors { errMsg += "\n- " + err.Error() } if notifier != nil { notifier.SendNotification("Errores en ejecución programada Obtener Codigos", errMsg) } log.Println(errMsg) } else { log.Println("Ejecución programada completada exitosamente Obtener Codigos") } }) if err != nil { log.Fatalf("Error al programar tareas Obtener Codigos: %v", err) } // Iniciar ambos cron c.Start() cronGetCode.Start() log.Printf("Demonio de servicios iniciado. Programación: %s", cfg.Schedule.FACTURA_SINCRONIZACION.Cron) log.Printf("Demonio de obtención de códigos iniciado. Programación: %s", cfg.Schedule.FACTURA_CODIGO.Cron) if notifier != nil { notifier.SendNotification("Servicios iniciados", fmt.Sprintf("Los servicios SOAP han sido iniciados con programación: %s", cfg.Schedule.FACTURA_SINCRONIZACION.Cron)) } // Esperar a que el contexto termine y luego detener ambos cron <-ctx.Done() c.Stop() cronGetCode.Stop() log.Println("Demonios detenidos correctamente") }