2025-05-17 11:36:26 -04:00

852 lines
30 KiB
Go

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(&registroID)
// 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
//}