package obtencionCodigo // //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 cuisServices []models.CuisService // 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) // // log.Println(cfg.SOAP.FACTURA_SINCRONIZACION.Retries) // 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, // APIKey: cfg.SOAP.APIKey, // 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, // APIKey: cfg.SOAP.APIKey, // TagNames: "", // QueryInsert: "", // MsgCustomResponse: "", // } // // //registrar-empresa solo para registrar empresa // cuisServiceModel.TagNames = "cuis" // 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 "leyendas_factura": // baseSvc.TagNames = "sincronizarListaLeyendasFactura" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // case "producto-servicio": // baseSvc.TagNames = "sincronizarListaProductosServicios" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // case "tipo_documento_identidad": // baseSvc.TagNames = "sincronizarParametricaTipoDocumentoIdentidad" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // case "tipo_documento_sector": // baseSvc.TagNames = "sincronizarParametricaTipoDocumentoSector" // baseSvc.QueryInsert = "INSERT INTO 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 de identidad" // servicesList = append(servicesList, &sincronizacionDatos.SincronizacionDatosService{ // ServiceModel: baseSvc, // }) // case "tipo_metodo_pago": // baseSvc.TagNames = "sincronizarParametricaTipoMetodoPago" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // case "tipo_moneda": // baseSvc.TagNames = "sincronizarParametricaTipoMoneda" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // case "tipo_punto_venta": // baseSvc.TagNames = "sincronizarParametricaTipoPuntoVenta" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // case "tipo_unidad_medida": // baseSvc.TagNames = "sincronizarParametricaUnidadMedida" // baseSvc.QueryInsert = "INSERT INTO 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, // }) // 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") //} //package obtencionCodigo // //import ( // "context" // "daemonService/internal/services/procesar" // "database/sql" // "encoding/json" // "errors" // "fmt" // "log" // "net/http" // "strconv" // "time" // // "daemonService/internal/models/obtencionCodigos" // "daemonService/internal/models/obtencionCodigos/request" // "daemonService/internal/models/obtencionCodigos/response" //) // //// Constantes para la configuración general //const ( // maxBodySize = 1 * 1024 * 1024 // 1MB // defaultTimeZone = "America/La_Paz" // logServiceStart = "Iniciando servicio %s" // logServiceEnd = "Finalizado servicio %s" // logErrorProcessing = "Error procesando registro %s: %v" //) // //// Errores comunes //var ( // ErrAllFieldsRequired = errors.New("todos los campos son requeridos") // ErrUnexpectedRespType = errors.New("tipo de respuesta inesperado") //) // //// mapea toda la tabla registroEmpresa //type RegistroEmpresa struct { // ID int `sql:"id"` // CodigoAmbiente int `sql:"codigo_ambiente"` // CodigoModalidad int `sql:"codigo_modalidad"` // CodigoPuntoVenta int `sql:"codigo_punto_venta"` // CodigoSistema string `sql:"codigo_sistema"` // CodigoSucursal int `sql:"codigo_sucursal"` // Nit string `sql:"nit"` // FechaCreacion time.Time `sql:"fecha_creacion"` // FechaActualizacion time.Time `sql:"fecha_actualizacion"` //} // //// solo tiene los dos campos que quieres de la tabla cuis //type CuisMinimal struct { // Cuis_id int64 `sql:"id"` // Cuis string `sql:"cuis"` // FechaVigencia time.Time `sql:"fecha_vigencia"` //} // //// EmpresaConCuis agrupa una empresa con sus cuis mínimos //type EmpresaConCuis struct { // RegistroEmpresa // Cuis []CuisMinimal //} // //type ObtencionCodigoService struct { // obtencionCodigos.CuisServiceModel // procesar.Procesar // Logger *log.Logger //} // //// retorna el nombre del servicio //func (s *ObtencionCodigoService) GetName() string { // return s.Name //} // //// registra un mensaje con el prefijo del servicio //func (s *ObtencionCodigoService) logMessage(format string, v ...interface{}) { // if s.Logger != nil { // s.Logger.Printf(format, v...) // } //} // //// registra un error y devuelve el mismo error //func (s *ObtencionCodigoService) logError(format string, err error, v ...interface{}) error { // args := append([]interface{}{err}, v...) // if s.Logger != nil { // s.Logger.Printf(format, args...) // } // return fmt.Errorf(format, args...) //} // //// maneja la obtención de códigos CUIS //func (s *ObtencionCodigoService) ExecuteGetCode(dbCtx context.Context, w http.ResponseWriter, req *http.Request, isCron bool) (interface{}, error) { // s.logMessage(logServiceStart, s.Name) // defer s.logMessage(logServiceEnd, s.Name) // // var respCuis interface{} // var err error // // if isCron { // respCuis, err = s.processCronMode(dbCtx) // } else { // respCuis, err = s.processAPIMode(dbCtx, w, req) // } // // if err != nil { // return nil, err // } // return respCuis, nil //} // //// procesa la ejecución en modo cron //func (s *ObtencionCodigoService) processCronMode(dbCtx context.Context) (interface{}, error) { // empresasConCuis, err := s.GetEmpresasConCuisMinimal(dbCtx) // if err != nil { // return nil, s.logError("Error al obtener empresas con CUIS: %v", err) // } // // var lastResponse interface{} // for _, ec := range empresasConCuis { // s.logMessage("Procesando empresa ID: %d", ec.ID) // // if len(ec.Cuis) == 0 { // s.logMessage("Empresa ID %d sin CUIS registrados", ec.ID) // continue // } // // for _, c := range ec.Cuis { // s.logMessage("Procesando CUIS ID: %d", c.Cuis_id) // // cuisRequest := s.buildCuisRequestFromEmpresa(ec) // respCuis, err := s.CodigoProcesarRegistro(&s.CuisServiceModel, cuisRequest, 0) // // if err != nil { // s.logError(logErrorProcessing, err, cuisRequest.CodigoSistema) // continue // } // // if err := s.actualizarRegistro(dbCtx, ec.ID, c.Cuis_id, cuisRequest, respCuis); err != nil { // s.logError("Error actualizando registro: %v", err) // continue // } // // lastResponse = respCuis // } // } // // return lastResponse, nil //} // //// procesa la ejecución en modo API //func (s *ObtencionCodigoService) processAPIMode(dbCtx context.Context, w http.ResponseWriter, req *http.Request) (interface{}, error) { // cuisRequest, err := s.parseCuisRequest(w, req) // if err != nil { // return nil, err // } // // if err := s.validateCuisRequest(cuisRequest); err != nil { // return nil, s.logError("Datos inválidos: %v", err) // } // // respCuis, err := s.CodigoProcesarRegistro(&s.CuisServiceModel, cuisRequest, 0) // if err != nil { // return nil, s.logError(logErrorProcessing, err, cuisRequest.CodigoSistema) // } // // if err := s.crearNuevoRegistro(dbCtx, cuisRequest, respCuis); err != nil { // return nil, s.logError("Error creando nuevo registro: %v", err) // } // // return respCuis, nil //} // //// parsea la solicitud de CUIS desde el body del request //func (s *ObtencionCodigoService) parseCuisRequest(w http.ResponseWriter, req *http.Request) (request.SolicitudCuis, error) { // var cuisRequest request.SolicitudCuis // // // Limitar el tamaño del body para prevenir ataques DoS // req.Body = http.MaxBytesReader(w, req.Body, maxBodySize) // // decoder := json.NewDecoder(req.Body) // decoder.DisallowUnknownFields() // Rechazar campos desconocidos // // if err := decoder.Decode(&cuisRequest); err != nil { // return cuisRequest, s.logError("Error al decodificar JSON: %v", err) // } // // return cuisRequest, nil //} // //// valida los datos de la solicitud CUIS //func (s *ObtencionCodigoService) validateCuisRequest(request request.SolicitudCuis) error { // if request.CodigoAmbiente == "" || // request.CodigoModalidad == "" || // request.CodigoPuntoVenta == "" || // request.CodigoSistema == "" || // request.CodigoSucursal == "" || // request.Nit == "" { // return ErrAllFieldsRequired // } // return nil //} // //// crea una solicitud CUIS a partir de datos de empresa //func (s *ObtencionCodigoService) buildCuisRequestFromEmpresa(ec EmpresaConCuis) request.SolicitudCuis { // return request.SolicitudCuis{ // CodigoAmbiente: strconv.Itoa(ec.CodigoAmbiente), // CodigoModalidad: strconv.Itoa(ec.CodigoModalidad), // CodigoPuntoVenta: strconv.Itoa(ec.CodigoPuntoVenta), // CodigoSistema: ec.CodigoSistema, // CodigoSucursal: strconv.Itoa(ec.CodigoSucursal), // Nit: ec.Nit, // } //} // //// obtiene todas las empresas con sus CUIS //func (s *ObtencionCodigoService) GetEmpresasConCuisMinimal(ctx context.Context) ([]EmpresaConCuis, error) { // const query = ` // SELECT // -- columnas de registroEmpresa // re.id, re.codigo_ambiente, re.codigo_modalidad, re.codigo_punto_venta, // re.codigo_sistema, re.codigo_sucursal, re.nit, // re.fecha_creacion, re.fecha_actualizacion, // -- columnas específicas de cuis // c.id, c.cuis, c.fecha_vigencia // FROM registroEmpresa re // LEFT JOIN cuis c ON c.registro_empresa_id = re.id // ORDER BY re.id; // ` // // rows, err := s.DB.QueryContext(ctx, query) // if err != nil { // return nil, fmt.Errorf("consulta empresas con cuis minimal: %w", err) // } // defer rows.Close() // // // Mapa temporal para agrupar por empresa.ID // empresasMap := make(map[int]*EmpresaConCuis) // // for rows.Next() { // // Variables temporales de escaneo // var ( // id int // codigoAmbiente int // codigoModalidad int // codigoPuntoVenta int // codigoSistema string // codigoSucursal int // nit string // fechaCreacion time.Time // fechaActualizacion time.Time // // idCuis sql.NullInt64 // cuis sql.NullString // fechaVigencia sql.NullTime // ) // // // Leer la fila // if err := rows.Scan( // &id, &codigoAmbiente, &codigoModalidad, &codigoPuntoVenta, // &codigoSistema, &codigoSucursal, &nit, // &fechaCreacion, &fechaActualizacion, // &idCuis, &cuis, &fechaVigencia, // ); err != nil { // return nil, fmt.Errorf("scan fila: %w", err) // } // // // ¿Ya existe la empresa en el mapa? // ent, ok := empresasMap[id] // if !ok { // // Si no existe, la creamos y rellenamos datos de registroEmpresa // ent = &EmpresaConCuis{ // RegistroEmpresa: RegistroEmpresa{ // ID: id, // CodigoAmbiente: codigoAmbiente, // CodigoModalidad: codigoModalidad, // CodigoPuntoVenta: codigoPuntoVenta, // CodigoSistema: codigoSistema, // CodigoSucursal: codigoSucursal, // Nit: nit, // FechaCreacion: fechaCreacion, // FechaActualizacion: fechaActualizacion, // }, // Cuis: make([]CuisMinimal, 0, 1), // } // empresasMap[id] = ent // } // // // Si hay un CUIS válido, lo añadimos al slice // if idCuis.Valid && cuis.Valid && fechaVigencia.Valid { // ent.Cuis = append(ent.Cuis, CuisMinimal{ // Cuis_id: idCuis.Int64, // Cuis: cuis.String, // FechaVigencia: fechaVigencia.Time, // }) // } // } // // if err := rows.Err(); err != nil { // return nil, fmt.Errorf("iteración filas: %w", err) // } // // // Convertimos el mapa a slice ordenado por aparición // resp := make([]EmpresaConCuis, 0, len(empresasMap)) // for _, ent := range empresasMap { // resp = append(resp, *ent) // } // return resp, nil //} // //// busca un registro de empresa por sus criterios //func (s *ObtencionCodigoService) buscarRegistroEmpresa(ctx context.Context, request request.SolicitudCuis) (int, error) { // query := ` // SELECT id // FROM registroEmpresa // WHERE codigo_sistema = $1 // AND nit = $2 // AND codigo_ambiente = $3 // AND codigo_punto_venta = $4 // AND codigo_sucursal = $5 // LIMIT 1 // ` // // var id int // err := s.DB.QueryRowContext( // ctx, // query, // request.CodigoSistema, // request.Nit, // request.CodigoAmbiente, // request.CodigoPuntoVenta, // request.CodigoSucursal, // ).Scan(&id) // // if err == sql.ErrNoRows { // return 0, nil // No existe el registro // } // // if err != nil { // return 0, err // } // // return id, nil //} // //// obtiene la hora actual en la zona horaria de Bolivia //func (s *ObtencionCodigoService) getCurrentBoliviaTime() (time.Time, error) { // loc, err := time.LoadLocation(defaultTimeZone) // if err != nil { // return time.Time{}, err // } // return time.Now().In(loc), nil //} // //// parsea la respuesta SOAP para CUIS //func (s *ObtencionCodigoService) parseSoapCuisResponse(respSoap interface{}) (string, string, bool, error) { // resp, ok := respSoap.(response.SoapBodyCuis) // if !ok { // return "", "", false, fmt.Errorf("%w: %T", ErrUnexpectedRespType, respSoap) // } // // return resp.Response.Respuesta.Codigo, // resp.Response.Respuesta.FechaVigencia, // resp.Response.Respuesta.Transaccion, // nil //} // //// crea un nuevo registro de empresa y CUIS //func (s *ObtencionCodigoService) crearNuevoRegistro(ctx context.Context, request request.SolicitudCuis, respSoap interface{}) error { // currentBolivia, err := s.getCurrentBoliviaTime() // if err != nil { // return err // } // // // Parsear respuesta // codigoCuis, fechaVigencia, transaccion, err := s.parseSoapCuisResponse(respSoap) // if err != nil { // return err // } // // // Iniciar transacción // tx, err := s.DB.BeginTx(ctx, nil) // if err != nil { // return fmt.Errorf("error al iniciar transacción: %w", err) // } // defer tx.Rollback() // // // Verificar si ya existe el NIT en la base de datos // var existeNIT bool // queryVerificacionNIT := ` // SELECT EXISTS ( // SELECT 1 FROM registroEmpresa // WHERE nit = $1 // ) // ` // err = tx.QueryRowContext( // ctx, // queryVerificacionNIT, // request.Nit, // ).Scan(&existeNIT) // // if err != nil { // return fmt.Errorf("error al verificar existencia del NIT: %w", err) // } // // // Si el NIT ya existe, verificar si la combinación de sucursal y punto de venta existe // if existeNIT { // var existeCombinacion bool // queryVerificacionCombinacion := ` // SELECT EXISTS ( // SELECT 1 FROM registroEmpresa // WHERE nit = $1 // AND codigo_sucursal = $2 // AND codigo_punto_venta = $3 // ) // ` // err = tx.QueryRowContext( // ctx, // queryVerificacionCombinacion, // request.Nit, // request.CodigoSucursal, // request.CodigoPuntoVenta, // ).Scan(&existeCombinacion) // // if err != nil { // return fmt.Errorf("error al verificar combinación de sucursal y punto de venta: %w", err) // } // // if existeCombinacion { // return fmt.Errorf("ya existe un registro con el mismo NIT, código sucursal y código punto de venta") // } // } // // // Insertar en registroEmpresa // query := ` // INSERT INTO registroEmpresa ( // codigo_ambiente, // codigo_modalidad, // codigo_punto_venta, // codigo_sistema, // codigo_sucursal, // nit // ) VALUES ($1, $2, $3, $4, $5, $6) // RETURNING id // ` // var registroID int // err = tx.QueryRowContext( // ctx, // query, // request.CodigoAmbiente, // request.CodigoModalidad, // request.CodigoPuntoVenta, // request.CodigoSistema, // request.CodigoSucursal, // request.Nit, // ).Scan(®istroID) // if err != nil { // return fmt.Errorf("error al insertar registro: %w", err) // } // // // Insertar en cuis // queryInsertCuis := ` // INSERT INTO cuis (cuis, fecha_vigencia, transaccion, fecha_creacion, registro_empresa_id) // VALUES ($1, $2, $3, $4, $5) // ` // _, err = tx.ExecContext(ctx, queryInsertCuis, codigoCuis, fechaVigencia, transaccion, currentBolivia, registroID) // if err != nil { // return fmt.Errorf("error al insertar CUIS: %w", err) // } // // if err = tx.Commit(); err != nil { // return fmt.Errorf("error al confirmar transacción: %w", err) // } // // return nil //} // //// actualiza un registro existente de empresa y CUIS //func (s *ObtencionCodigoService) actualizarRegistro(ctx context.Context, empresaID int, cuisID int64, req request.SolicitudCuis, respRaw interface{}) error { // currentBolivia, err := s.getCurrentBoliviaTime() // if err != nil { // return err // } // // // 1. Iniciar tx // tx, err := s.DB.BeginTx(ctx, nil) // if err != nil { // return fmt.Errorf("iniciar transacción: %w", err) // } // defer tx.Rollback() // // // 2. Parsear respuesta // nuevoCuis, fechaVigencia, _, err := s.parseSoapCuisResponse(respRaw) // if err != nil { // return err // } // // // 3. Actualizar empresa // _, err = tx.ExecContext(ctx, ` // UPDATE registroEmpresa // SET codigo_ambiente=$1, codigo_modalidad=$2, codigo_punto_venta=$3, // codigo_sistema=$4, codigo_sucursal=$5, nit=$6, fecha_actualizacion=$7 // WHERE id=$8 // `, // req.CodigoAmbiente, req.CodigoModalidad, req.CodigoPuntoVenta, // req.CodigoSistema, req.CodigoSucursal, req.Nit, currentBolivia, empresaID, // ) // if err != nil { // return fmt.Errorf("actualizar registroEmpresa: %w", err) // } // // // 4. Comprobar si hay un CUIS existente // var existingCuis string // err = tx.QueryRowContext(ctx, `SELECT cuis FROM cuis WHERE id=$1 AND registro_empresa_id=$2`, cuisID, empresaID).Scan(&existingCuis) // if err != nil && err != sql.ErrNoRows { // return fmt.Errorf("consultar cuis existente: %w", err) // } // // // 5. Insertar o actualizar // if err == sql.ErrNoRows { // _, err = tx.ExecContext(ctx, `INSERT INTO cuis (registro_empresa_id, cuis) VALUES ($1, $2)`, empresaID, nuevoCuis) // if err != nil { // return fmt.Errorf("insertar nuevo cuis: %w", err) // } // } else if existingCuis != nuevoCuis { // _, err = tx.ExecContext(ctx, `UPDATE cuis SET cuis=$1, fecha_vigencia=$2, fecha_actualizacion=$3 WHERE id=$4`, // nuevoCuis, fechaVigencia, currentBolivia, cuisID) // if err != nil { // return fmt.Errorf("actualizar cuis existente: %w", err) // } // } // // // 6. Confirmar tx // if err := tx.Commit(); err != nil { // return fmt.Errorf("confirmar transacción: %w", err) // } // return nil //}