// // domain/models.go package obtencionCodigo // //import ( // "time" //) // //// RegistroEmpresa 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"` //} // //// CuisMinimal 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 //} //// domain/errors.go //package domain // //import "errors" // //// Errores comunes //var ( // ErrAllFieldsRequired = errors.New("todos los campos son requeridos") // ErrUnexpectedRespType = errors.New("tipo de respuesta inesperado") // ErrDuplicateRecord = errors.New("ya existe un registro con el mismo NIT, código sucursal y código punto de venta") //) //// utils/constants.go //package utils // //// 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" //) //// utils/time_utils.go //package utils // //import ( //"time" //) // //// GetCurrentBoliviaTime obtiene la hora actual en la zona horaria de Bolivia //func GetCurrentBoliviaTime() (time.Time, error) { // loc, err := time.LoadLocation(DefaultTimeZone) // if err != nil { // return time.Time{}, err // } // return time.Now().In(loc), nil //} //// utils/soap_utils.go //package utils // //import ( //"fmt" // //"daemonService/internal/models/obtencionCodigos/response" //"obtencionCodigo/domain" //) // //// ParseSoapCuisResponse parsea la respuesta SOAP para CUIS //func ParseSoapCuisResponse(respSoap interface{}) (string, string, bool, error) { // resp, ok := respSoap.(response.SoapBodyCuis) // if !ok { // return "", "", false, fmt.Errorf("%w: %T", domain.ErrUnexpectedRespType, respSoap) // } // // return resp.Response.Respuesta.Codigo, // resp.Response.Respuesta.FechaVigencia, // resp.Response.Respuesta.Transaccion, // nil //} // repository/empresa_repo.go //package repository // //import ( //"context" //"database/sql" //"fmt" //"time" // //"obtencionCodigo/domain" //"daemonService/internal/models/obtencionCodigos/request" //) // //type EmpresaRepository struct { // DB *sql.DB //} // //// GetEmpresasConCuisMinimal obtiene todas las empresas con sus CUIS //func (r *EmpresaRepository) GetEmpresasConCuisMinimal(ctx context.Context) ([]domain.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 := r.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]*domain.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 = &domain.EmpresaConCuis{ // RegistroEmpresa: domain.RegistroEmpresa{ // ID: id, // CodigoAmbiente: codigoAmbiente, // CodigoModalidad: codigoModalidad, // CodigoPuntoVenta: codigoPuntoVenta, // CodigoSistema: codigoSistema, // CodigoSucursal: codigoSucursal, // Nit: nit, // FechaCreacion: fechaCreacion, // FechaActualizacion: fechaActualizacion, // }, // Cuis: make([]domain.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, domain.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([]domain.EmpresaConCuis, 0, len(empresasMap)) // for _, ent := range empresasMap { // resp = append(resp, *ent) // } // return resp, nil //} // //// BuscarRegistroEmpresa busca un registro de empresa por sus criterios //func (r *EmpresaRepository) 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 := r.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 //} //// repository/cuis_repo.go //package repository // //import ( //"context" //"database/sql" //"fmt" //"time" // //"obtencionCodigo/domain" //"obtencionCodigo/utils" //"daemonService/internal/models/obtencionCodigos/request" //) // //type CuisRepository struct { // DB *sql.DB //} // //// CrearNuevoRegistro crea un nuevo registro de empresa y CUIS //func (r *CuisRepository) CrearNuevoRegistro(ctx context.Context, request request.SolicitudCuis, codigoCuis string, fechaVigencia string, transaccion bool) error { // currentBolivia, err := utils.GetCurrentBoliviaTime() // if err != nil { // return err // } // // // Iniciar transacción // tx, err := r.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 domain.ErrDuplicateRecord // } // } // // // 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 //} // //// ActualizarRegistro actualiza un registro existente de empresa y CUIS //func (r *CuisRepository) ActualizarRegistro(ctx context.Context, empresaID int, cuisID int64, req request.SolicitudCuis, nuevoCuis string, fechaVigencia string) error { // currentBolivia, err := utils.GetCurrentBoliviaTime() // if err != nil { // return err // } // // // Iniciar tx // tx, err := r.DB.BeginTx(ctx, nil) // if err != nil { // return fmt.Errorf("iniciar transacción: %w", err) // } // defer tx.Rollback() // // // 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) // } // // // 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) // } // // // 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) // } // } // // // Confirmar tx // if err := tx.Commit(); err != nil { // return fmt.Errorf("confirmar transacción: %w", err) // } // return nil //} //// service/cuis_service.go //package service // //import ( //"context" //"fmt" //"log" //"strconv" // //"obtencionCodigo/domain" //"obtencionCodigo/repository" //"obtencionCodigo/utils" //"daemonService/internal/models/obtencionCodigos" //"daemonService/internal/models/obtencionCodigos/request" //) // //// Función externa que se supone que existe en el proyecto original //// Esta es una aproximación, ya que no tenemos el código fuente //var codigoProcesarRegistro = func(model *obtencionCodigos.CuisServiceModel, request request.SolicitudCuis, intentos int) (interface{}, error) { // // Esta función debe implementarse según el código original // return nil, nil //} // //type CuisService struct { // Model *obtencionCodigos.CuisServiceModel // EmpresaRepo *repository.EmpresaRepository // CuisRepo *repository.CuisRepository // Logger *log.Logger //} // //// GetName retorna el nombre del servicio //func (s *CuisService) GetName() string { // return s.Model.Name //} // //// logMessage registra un mensaje con el prefijo del servicio //func (s *CuisService) logMessage(format string, v ...interface{}) { // if s.Logger != nil { // s.Logger.Printf(format, v...) // } //} // //// logError registra un error y devuelve el mismo error //func (s *CuisService) 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...) //} // //// ValidateCuisRequest valida los datos de la solicitud CUIS //func (s *CuisService) ValidateCuisRequest(request request.SolicitudCuis) error { // if request.CodigoAmbiente == "" || // request.CodigoModalidad == "" || // request.CodigoPuntoVenta == "" || // request.CodigoSistema == "" || // request.CodigoSucursal == "" || // request.Nit == "" { // return domain.ErrAllFieldsRequired // } // return nil //} // //// BuildCuisRequestFromEmpresa crea una solicitud CUIS a partir de datos de empresa //func (s *CuisService) BuildCuisRequestFromEmpresa(ec domain.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, // } //} // //// ProcessEmpresa procesa una empresa y actualiza su CUIS si es necesario //func (s *CuisService) ProcessEmpresa(ctx context.Context, ec domain.EmpresaConCuis) (interface{}, error) { // s.logMessage("Procesando empresa ID: %d", ec.ID) // // if len(ec.Cuis) == 0 { // s.logMessage("Empresa ID %d sin CUIS registrados", ec.ID) // return nil, nil // } // // var lastResponse interface{} // for _, c := range ec.Cuis { // s.logMessage("Procesando CUIS ID: %d", c.Cuis_id) // // cuisRequest := s.BuildCuisRequestFromEmpresa(ec) // respCuis, err := codigoProcesarRegistro(s.Model, cuisRequest, 0) // // if err != nil { // s.logError(utils.LogErrorProcessing, err, cuisRequest.CodigoSistema) // continue // } // // // Parsear respuesta SOAP // nuevoCuis, fechaVigencia, transaccion, err := utils.ParseSoapCuisResponse(respCuis) // if err != nil { // s.logError("Error al parsear respuesta SOAP: %v", err) // continue // } // // if err := s.CuisRepo.ActualizarRegistro(ctx, ec.ID, c.Cuis_id, cuisRequest, nuevoCuis, fechaVigencia); err != nil { // s.logError("Error actualizando registro: %v", err) // continue // } // // lastResponse = respCuis // } // // return lastResponse, nil //} // //// handler/api_handler.go //package handler // //import ( //"context" //"encoding/json" //"fmt" //"net/http" // //"obtencionCodigo/service" //"obtencionCodigo/utils" //"daemonService/internal/models/obtencionCodigos/request" //) // //type APIHandler struct { // CuisService *service.CuisService //} // //// ParseCuisRequest parsea la solicitud de CUIS desde el body del request //func (h *APIHandler) 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, utils.MaxBodySize) // // decoder := json.NewDecoder(req.Body) // decoder.DisallowUnknownFields() // Rechazar campos desconocidos // // if err := decoder.Decode(&cuisRequest); err != nil { // return cuisRequest, fmt.Errorf("error al decodificar JSON: %v", err) // } // // return cuisRequest, nil //} // //// HandleAPIRequest maneja las solicitudes API //func (h *APIHandler) HandleAPIRequest(ctx context.Context, w http.ResponseWriter, req *http.Request) (interface{}, error) { // // Parsear la solicitud // cuisRequest, err := h.ParseCuisRequest(w, req) // if err != nil { // return nil, err // } // // // Validar la solicitud // if err := h.CuisService.ValidateCuisRequest(cuisRequest); err != nil { // return nil, fmt.Errorf("datos inválidos: %v", err) // } // // // Procesar la solicitud // respCuis, err := codigoProcesarRegistro(h.CuisService.Model, cuisRequest, 0) // if err != nil { // return nil, fmt.Errorf(utils.LogErrorProcessing, cuisRequest.CodigoSistema, err) // } // // // Parsear respuesta SOAP // codigoCuis, fechaVigencia, transaccion, err := utils.ParseSoapCuisResponse(respCuis) // if err != nil { // return nil, fmt.Errorf("error al parsear respuesta SOAP: %v", err) // } // // // Crear nuevo registro // if err := h.CuisService.CuisRepo.CrearNuevoRegistro(ctx, cuisRequest, codigoCuis, fechaVigencia, transaccion); err != nil { // return nil, fmt.Errorf("error creando nuevo registro: %v", err) // } // // return respCuis, nil //} //// handler/cron_handler.go //package handler // //import ( //"context" // //"obtencionCodigo/service" //) // //type CronHandler struct { // CuisService *service.CuisService //} // //// HandleCronRequest maneja las ejecuciones programadas //func (h *CronHandler) HandleCronRequest(ctx context.Context) (interface{}, error) { // // Obtener todas las empresas con sus CUIS // empresasConCuis, err := h.CuisService.EmpresaRepo.GetEmpresasConCuisMinimal(ctx) // if err != nil { // return nil, fmt.Errorf("error al obtener empresas con CUIS: %v", err) // } // // var lastResponse interface{} // // Procesar cada empresa // for _, ec := range empresasConCuis { // resp, err := h.CuisService.ProcessEmpresa(ctx, ec) // if err != nil { // h.CuisService.logError("Error procesando empresa %d: %v", err, ec.ID) // continue // } // if resp != nil { // lastResponse = resp // } // } // // return lastResponse, nil //} //// service.go (punto de entrada principal) //package obtencionCodigo // //import ( //"context" //"log" //"net/http" // //"obtencionCodigo/domain" //"obtencionCodigo/repository" //"obtencionCodigo/service" //"obtencionCodigo/handler" //"obtencionCodigo/utils" //"daemonService/internal/models/obtencionCodigos" //) // //type ObtencionCodigoService struct { // Model obtencionCodigos.CuisServiceModel // Logger *log.Logger // EmpresaRepo *repository.EmpresaRepository // CuisRepo *repository.CuisRepository // CuisService *service.CuisService // APIHandler *handler.APIHandler // CronHandler *handler.CronHandler //} // //// NewObtencionCodigoService crea una nueva instancia del servicio //func NewObtencionCodigoService(model obtencionCodigos.CuisServiceModel, logger *log.Logger) *ObtencionCodigoService { // empresaRepo := &repository.EmpresaRepository{DB: model.DB} // cuisRepo := &repository.CuisRepository{DB: model.DB} // // cuisService := &service.CuisService{ // Model: &model, // EmpresaRepo: empresaRepo, // CuisRepo: cuisRepo, // Logger: logger, // } // // return &ObtencionCodigoService{ // Model: model, // Logger: logger, // EmpresaRepo: empresaRepo, // CuisRepo: cuisRepo, // CuisService: cuisService, // APIHandler: &handler.APIHandler{CuisService: cuisService}, // CronHandler: &handler.CronHandler{CuisService: cuisService}, // } //} // //// GetName retorna el nombre del servicio //func (s *ObtencionCodigoService) GetName() string { // return s.Model.Name //} // //// ExecuteGetCode 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.Logger.Printf(utils.LogServiceStart, s.GetName()) // defer s.Logger.Printf(utils.LogServiceEnd, s.GetName()) // // var respCuis interface{} // var err error // // if isCron { // respCuis, err = s.CronHandler.HandleCronRequest(dbCtx) // } else { // respCuis, err = s.APIHandler.HandleAPIRequest(dbCtx, w, req) // } // // if err != nil { // return nil, err // } // return respCuis, nil //}