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

716 lines
20 KiB
Go

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