671 lines
23 KiB
Go
671 lines
23 KiB
Go
// internal/soap/client.go
|
|
package soap
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"consumer/internal/logging"
|
|
"consumer/internal/models/entidad_db"
|
|
soapModels "consumer/internal/models/soap"
|
|
"consumer/internal/utils"
|
|
)
|
|
|
|
// SOAPClient maneja las interacciones con el servicio SOAP
|
|
type SOAPClient struct {
|
|
client *http.Client
|
|
logger *logging.LoggerSystem
|
|
SoapURL string // Cambiado a público para acceder desde otros paquetes
|
|
}
|
|
|
|
// NewSOAPClient crea un nuevo cliente SOAP
|
|
func NewSOAPClient(soapURL string, logger *logging.LoggerSystem) *SOAPClient {
|
|
transport := &http.Transport{
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
ResponseHeaderTimeout: 20 * time.Second,
|
|
ExpectContinueTimeout: 5 * time.Second,
|
|
DisableKeepAlives: false,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
MaxIdleConns: 100,
|
|
MaxIdleConnsPerHost: 10,
|
|
}
|
|
|
|
return &SOAPClient{
|
|
client: &http.Client{
|
|
Timeout: 60 * time.Second, // Reducir de 120s a 60s
|
|
Transport: transport,
|
|
},
|
|
logger: logger,
|
|
SoapURL: soapURL, // Ahora usando campo público
|
|
}
|
|
}
|
|
|
|
// parseSOAPResponse extrae la respuesta de la estructura SOAP
|
|
func (sc *SOAPClient) parseSOAPResponse(respBytes []byte) (soapModels.RespuestaServicioFacturacion, error) {
|
|
var env struct {
|
|
Body struct {
|
|
Content []byte `xml:",innerxml"`
|
|
} `xml:"Body"`
|
|
}
|
|
|
|
if err := xml.Unmarshal(respBytes, &env); err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, fmt.Errorf("error parseando envelope: %w", err)
|
|
}
|
|
|
|
var wrap soapModels.RecepcionFacturaResponse
|
|
if err := xml.Unmarshal(env.Body.Content, &wrap); err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, fmt.Errorf("error parseando respuesta: %w", err)
|
|
}
|
|
|
|
return wrap.RespuestaServicioFacturacion, nil
|
|
}
|
|
|
|
// callService envía el XML firmado al servicio SOAP de SIAT
|
|
func (sc *SOAPClient) CallService(
|
|
ctx context.Context,
|
|
registro entidad_db.DatosFactura,
|
|
firmaXML, hashArchivo string,
|
|
) (soapModels.RespuestaServicioFacturacion, int, string, string, int64, error) {
|
|
// Construir la solicitud
|
|
inner := soapModels.SolicitudServicioRecepcionFactura{
|
|
CodigoAmbiente: registro.CodigoAmbiente,
|
|
CodigoDocumentoSector: 1,
|
|
CodigoEmision: 1,
|
|
CodigoModalidad: registro.CodigoModalidad,
|
|
CodigoPuntoVenta: registro.CodigoPuntoVenta,
|
|
CodigoSistema: registro.CodigoSistema,
|
|
CodigoSucursal: registro.CodigoSucursal,
|
|
CUFD: registro.CUFD,
|
|
CUIS: registro.CUIS,
|
|
NIT: registro.NIT,
|
|
TipoFacturaDocumento: 1,
|
|
Archivo: firmaXML,
|
|
FechaEnvio: utils.NowInBolivia().Format("2006-01-02T15:04:05.000"),
|
|
HashArchivo: hashArchivo,
|
|
}
|
|
|
|
env := soapModels.RecepcionFacturaEnvelope{
|
|
SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Siat: "https://siat.impuestos.gob.bo/",
|
|
}
|
|
env.Body.Recepcion.Solicitud = inner
|
|
|
|
payload, err := xml.MarshalIndent(env, "", " ")
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, "", "", 0, fmt.Errorf("error marshalling XML: %w", err)
|
|
}
|
|
|
|
fullReq := xml.Header + string(payload)
|
|
|
|
// Crear la solicitud HTTP con el contexto
|
|
httpReq, err := http.NewRequestWithContext(ctx, "POST", sc.SoapURL, bytes.NewReader([]byte(fullReq)))
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", 0, fmt.Errorf("error creando request: %w", err)
|
|
}
|
|
|
|
// Configurar exactamente los mismos encabezados que en el curl exitoso
|
|
httpReq.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
|
httpReq.Header.Set("Accept", "text/xml")
|
|
httpReq.Header.Set(registro.TokenKey, registro.TokenValue)
|
|
|
|
// Realizar la solicitud con timeout aumentado
|
|
start := utils.NowInBolivia()
|
|
client := &http.Client{
|
|
Timeout: 120 * time.Second, // Aumentar a 2 minutos
|
|
Transport: &http.Transport{
|
|
TLSHandshakeTimeout: 30 * time.Second,
|
|
ResponseHeaderTimeout: 30 * time.Second,
|
|
ExpectContinueTimeout: 10 * time.Second,
|
|
DisableKeepAlives: false,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(httpReq)
|
|
latency := time.Since(start).Milliseconds()
|
|
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", latency, fmt.Errorf("error llamando al servicio: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, "", latency, fmt.Errorf("error leyendo respuesta: %w", err)
|
|
}
|
|
respStr := string(respBytes)
|
|
|
|
result, err := sc.parseSOAPResponse(respBytes)
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parseando SOAP: %w", err)
|
|
}
|
|
|
|
return result, resp.StatusCode, fullReq, respStr, latency, nil
|
|
}
|
|
|
|
// CallVerificacionEstadoFactura sends a request to verify invoice status
|
|
func (sc *SOAPClient) CallVerificacionEstadoFactura(
|
|
ctx context.Context,
|
|
registro entidad_db.DatosFactura,
|
|
cuf string,
|
|
) (soapModels.RespuestaServicioFacturacion, int, string, string, int64, error) {
|
|
// Construir la solicitud
|
|
inner := soapModels.SolicitudServicioVerificacionEstadoFactura{
|
|
CodigoAmbiente: registro.CodigoAmbiente,
|
|
CodigoDocumentoSector: 1,
|
|
CodigoEmision: 1,
|
|
CodigoModalidad: registro.CodigoModalidad,
|
|
CodigoPuntoVenta: registro.CodigoPuntoVenta,
|
|
CodigoSistema: registro.CodigoSistema,
|
|
CodigoSucursal: registro.CodigoSucursal,
|
|
CUFD: registro.CUFD,
|
|
CUIS: registro.CUIS,
|
|
NIT: registro.NIT,
|
|
TipoFacturaDocumento: 1,
|
|
CUF: cuf,
|
|
}
|
|
|
|
env := soapModels.VerificacionEstadoFacturaEnvelope{
|
|
SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Siat: "https://siat.impuestos.gob.bo/",
|
|
}
|
|
env.Body.Verificacion.Solicitud = inner
|
|
|
|
payload, err := xml.MarshalIndent(env, "", " ")
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, "", "", 0, fmt.Errorf("error marshalling XML: %w", err)
|
|
}
|
|
|
|
fullReq := xml.Header + string(payload)
|
|
|
|
// Crear la solicitud HTTP con el contexto
|
|
httpReq, err := http.NewRequestWithContext(ctx, "POST", sc.SoapURL, bytes.NewReader([]byte(fullReq)))
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", 0, fmt.Errorf("error creando request: %w", err)
|
|
}
|
|
|
|
// Configurar encabezados
|
|
httpReq.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
|
httpReq.Header.Set("Accept", "text/xml")
|
|
httpReq.Header.Set(registro.TokenKey, registro.TokenValue)
|
|
|
|
// Realizar la solicitud con timeout aumentado
|
|
start := utils.NowInBolivia()
|
|
client := &http.Client{
|
|
Timeout: 120 * time.Second,
|
|
Transport: &http.Transport{
|
|
TLSHandshakeTimeout: 30 * time.Second,
|
|
ResponseHeaderTimeout: 30 * time.Second,
|
|
ExpectContinueTimeout: 10 * time.Second,
|
|
DisableKeepAlives: false,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(httpReq)
|
|
latency := time.Since(start).Milliseconds()
|
|
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", latency, fmt.Errorf("error llamando al servicio: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, "", latency, fmt.Errorf("error leyendo respuesta: %w", err)
|
|
}
|
|
respStr := string(respBytes)
|
|
|
|
// Parsear la respuesta
|
|
var soapEnv struct {
|
|
Body struct {
|
|
Content []byte `xml:",innerxml"`
|
|
} `xml:"Body"`
|
|
}
|
|
|
|
if err := xml.Unmarshal(respBytes, &soapEnv); err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parseando envelope: %w", err)
|
|
}
|
|
|
|
var wrap soapModels.VerificacionEstadoFacturaResponse
|
|
if err := xml.Unmarshal(soapEnv.Body.Content, &wrap); err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parseando respuesta: %w", err)
|
|
}
|
|
|
|
return wrap.RespuestaServicioFacturacion, resp.StatusCode, fullReq, respStr, latency, nil
|
|
}
|
|
|
|
// CallAnulacionFactura sends a cancellation request to the SOAP service
|
|
func (sc *SOAPClient) CallAnulacionFactura(
|
|
ctx context.Context,
|
|
registro entidad_db.DatosFactura,
|
|
cuf string,
|
|
codigoMotivo int,
|
|
) (soapModels.RespuestaServicioFacturacion, int, string, string, int64, error) {
|
|
// Build the request
|
|
inner := soapModels.SolicitudServicioAnulacionFactura{
|
|
CodigoAmbiente: registro.CodigoAmbiente,
|
|
CodigoDocumentoSector: 1,
|
|
CodigoEmision: 1,
|
|
CodigoModalidad: registro.CodigoModalidad,
|
|
CodigoPuntoVenta: registro.CodigoPuntoVenta,
|
|
CodigoSistema: registro.CodigoSistema,
|
|
CodigoSucursal: registro.CodigoSucursal,
|
|
CUFD: registro.CUFD,
|
|
CUIS: registro.CUIS,
|
|
NIT: registro.NIT,
|
|
TipoFacturaDocumento: 1,
|
|
CodigoMotivo: codigoMotivo, // Default is 1, can be made configurable
|
|
CUF: cuf,
|
|
}
|
|
|
|
env := soapModels.AnulacionFacturaEnvelope{
|
|
SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Siat: "https://siat.impuestos.gob.bo/",
|
|
}
|
|
env.Body.Anulacion.Solicitud = inner
|
|
|
|
payload, err := xml.MarshalIndent(env, "", " ")
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, "", "", 0, fmt.Errorf("error marshalling XML: %w", err)
|
|
}
|
|
|
|
fullReq := xml.Header + string(payload)
|
|
|
|
// Create HTTP request with context
|
|
httpReq, err := http.NewRequestWithContext(ctx, "POST", sc.SoapURL, bytes.NewReader([]byte(fullReq)))
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", 0, fmt.Errorf("error creating request: %w", err)
|
|
}
|
|
|
|
// Set headers
|
|
httpReq.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
|
httpReq.Header.Set("Accept", "text/xml")
|
|
httpReq.Header.Set(registro.TokenKey, registro.TokenValue)
|
|
|
|
// Send request with increased timeout
|
|
start := utils.NowInBolivia()
|
|
client := &http.Client{
|
|
Timeout: 120 * time.Second,
|
|
Transport: &http.Transport{
|
|
TLSHandshakeTimeout: 30 * time.Second,
|
|
ResponseHeaderTimeout: 30 * time.Second,
|
|
ExpectContinueTimeout: 10 * time.Second,
|
|
DisableKeepAlives: false,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(httpReq)
|
|
latency := time.Since(start).Milliseconds()
|
|
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", latency, fmt.Errorf("error calling service: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, "", latency, fmt.Errorf("error reading response: %w", err)
|
|
}
|
|
respStr := string(respBytes)
|
|
|
|
// Parse response
|
|
var soapEnv struct {
|
|
Body struct {
|
|
Content []byte `xml:",innerxml"`
|
|
} `xml:"Body"`
|
|
}
|
|
|
|
if err := xml.Unmarshal(respBytes, &soapEnv); err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parsing envelope: %w", err)
|
|
}
|
|
|
|
var wrap soapModels.AnulacionFacturaResponse
|
|
if err := xml.Unmarshal(soapEnv.Body.Content, &wrap); err != nil {
|
|
return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parsing response: %w", err)
|
|
}
|
|
|
|
return wrap.RespuestaServicioFacturacion, resp.StatusCode, fullReq, respStr, latency, nil
|
|
}
|
|
|
|
//package soap
|
|
//
|
|
//import (
|
|
// "bytes"
|
|
// "context"
|
|
// "encoding/xml"
|
|
// "fmt"
|
|
// "io"
|
|
// "net/http"
|
|
// "time"
|
|
//
|
|
// "consumer/internal/logging"
|
|
// "consumer/internal/models/entidad_db"
|
|
// soapModels "consumer/internal/models/soap"
|
|
// "consumer/internal/utils"
|
|
//)
|
|
//
|
|
//// SOAPClient maneja las interacciones con el servicio SOAP
|
|
//type SOAPClient struct {
|
|
// client *http.Client
|
|
// logger *logging.LoggerSystem
|
|
// soapURL string
|
|
//}
|
|
//
|
|
//// NewSOAPClient crea un nuevo cliente SOAP
|
|
//func NewSOAPClient(soapURL string, logger *logging.LoggerSystem) *SOAPClient {
|
|
// transport := &http.Transport{
|
|
// TLSHandshakeTimeout: 10 * time.Second,
|
|
// ResponseHeaderTimeout: 20 * time.Second,
|
|
// ExpectContinueTimeout: 5 * time.Second,
|
|
// DisableKeepAlives: false,
|
|
// IdleConnTimeout: 90 * time.Second,
|
|
// MaxIdleConns: 100,
|
|
// MaxIdleConnsPerHost: 10,
|
|
// }
|
|
//
|
|
// return &SOAPClient{
|
|
// client: &http.Client{
|
|
// Timeout: 60 * time.Second, // Reducir de 120s a 60s
|
|
// Transport: transport,
|
|
// },
|
|
// logger: logger,
|
|
// soapURL: soapURL,
|
|
// }
|
|
//}
|
|
//
|
|
//// parseSOAPResponse extrae la respuesta de la estructura SOAP
|
|
//func (sc *SOAPClient) parseSOAPResponse(respBytes []byte) (soapModels.RespuestaServicioFacturacion, error) {
|
|
// var env struct {
|
|
// Body struct {
|
|
// Content []byte `xml:",innerxml"`
|
|
// } `xml:"Body"`
|
|
// }
|
|
//
|
|
// if err := xml.Unmarshal(respBytes, &env); err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, fmt.Errorf("error parseando envelope: %w", err)
|
|
// }
|
|
//
|
|
// var wrap soapModels.RecepcionFacturaResponse
|
|
// if err := xml.Unmarshal(env.Body.Content, &wrap); err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, fmt.Errorf("error parseando respuesta: %w", err)
|
|
// }
|
|
//
|
|
// return wrap.RespuestaServicioFacturacion, nil
|
|
//}
|
|
//
|
|
//// callService envía el XML firmado al servicio SOAP de SIAT
|
|
//func (sc *SOAPClient) CallService(
|
|
// ctx context.Context,
|
|
// registro entidad_db.DatosFactura,
|
|
// firmaXML, hashArchivo string,
|
|
//) (soapModels.RespuestaServicioFacturacion, int, string, string, int64, error) {
|
|
// // Construir la solicitud
|
|
// inner := soapModels.SolicitudServicioRecepcionFactura{
|
|
// CodigoAmbiente: registro.CodigoAmbiente,
|
|
// CodigoDocumentoSector: 1,
|
|
// CodigoEmision: 1,
|
|
// CodigoModalidad: registro.CodigoModalidad,
|
|
// CodigoPuntoVenta: registro.CodigoPuntoVenta,
|
|
// CodigoSistema: registro.CodigoSistema,
|
|
// CodigoSucursal: registro.CodigoSucursal,
|
|
// CUFD: registro.CUFD,
|
|
// CUIS: registro.CUIS,
|
|
// NIT: registro.NIT,
|
|
// TipoFacturaDocumento: 1,
|
|
// Archivo: firmaXML,
|
|
// FechaEnvio: utils.NowInBolivia().Format("2006-01-02T15:04:05.000"),
|
|
// HashArchivo: hashArchivo,
|
|
// }
|
|
//
|
|
// env := soapModels.RecepcionFacturaEnvelope{
|
|
// SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
// Siat: "https://siat.impuestos.gob.bo/",
|
|
// }
|
|
// env.Body.Recepcion.Solicitud = inner
|
|
//
|
|
// payload, err := xml.MarshalIndent(env, "", " ")
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, "", "", 0, fmt.Errorf("error marshalling XML: %w", err)
|
|
// }
|
|
//
|
|
// fullReq := xml.Header + string(payload)
|
|
//
|
|
// // Crear la solicitud HTTP con el contexto
|
|
// httpReq, err := http.NewRequestWithContext(ctx, "POST", sc.soapURL, bytes.NewReader([]byte(fullReq)))
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", 0, fmt.Errorf("error creando request: %w", err)
|
|
// }
|
|
//
|
|
// // Configurar exactamente los mismos encabezados que en el curl exitoso
|
|
// httpReq.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
|
// httpReq.Header.Set("Accept", "text/xml")
|
|
// httpReq.Header.Set(registro.TokenKey, registro.TokenValue)
|
|
//
|
|
// // Realizar la solicitud con timeout aumentado
|
|
// start := utils.NowInBolivia()
|
|
// client := &http.Client{
|
|
// Timeout: 120 * time.Second, // Aumentar a 2 minutos
|
|
// Transport: &http.Transport{
|
|
// TLSHandshakeTimeout: 30 * time.Second,
|
|
// ResponseHeaderTimeout: 30 * time.Second,
|
|
// ExpectContinueTimeout: 10 * time.Second,
|
|
// DisableKeepAlives: false,
|
|
// IdleConnTimeout: 90 * time.Second,
|
|
// },
|
|
// }
|
|
//
|
|
// resp, err := client.Do(httpReq)
|
|
// latency := time.Since(start).Milliseconds()
|
|
//
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", latency, fmt.Errorf("error llamando al servicio: %w", err)
|
|
// }
|
|
// defer resp.Body.Close()
|
|
//
|
|
// respBytes, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, "", latency, fmt.Errorf("error leyendo respuesta: %w", err)
|
|
// }
|
|
// respStr := string(respBytes)
|
|
//
|
|
// result, err := sc.parseSOAPResponse(respBytes)
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parseando SOAP: %w", err)
|
|
// }
|
|
//
|
|
// return result, resp.StatusCode, fullReq, respStr, latency, nil
|
|
//}
|
|
//
|
|
//// CallVerificacionEstadoFactura sends a request to verify invoice status
|
|
//func (sc *SOAPClient) CallVerificacionEstadoFactura(
|
|
// ctx context.Context,
|
|
// registro entidad_db.DatosFactura,
|
|
// cuf string,
|
|
//) (soapModels.RespuestaServicioFacturacion, int, string, string, int64, error) {
|
|
// // Construir la solicitud
|
|
// inner := soapModels.SolicitudServicioVerificacionEstadoFactura{
|
|
// CodigoAmbiente: registro.CodigoAmbiente,
|
|
// CodigoDocumentoSector: 1,
|
|
// CodigoEmision: 1,
|
|
// CodigoModalidad: registro.CodigoModalidad,
|
|
// CodigoPuntoVenta: registro.CodigoPuntoVenta,
|
|
// CodigoSistema: registro.CodigoSistema,
|
|
// CodigoSucursal: registro.CodigoSucursal,
|
|
// CUFD: registro.CUFD,
|
|
// CUIS: registro.CUIS,
|
|
// NIT: registro.NIT,
|
|
// TipoFacturaDocumento: 1,
|
|
// CUF: cuf,
|
|
// }
|
|
//
|
|
// env := soapModels.VerificacionEstadoFacturaEnvelope{
|
|
// SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
// Siat: "https://siat.impuestos.gob.bo/",
|
|
// }
|
|
// env.Body.Verificacion.Solicitud = inner
|
|
//
|
|
// payload, err := xml.MarshalIndent(env, "", " ")
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, "", "", 0, fmt.Errorf("error marshalling XML: %w", err)
|
|
// }
|
|
//
|
|
// fullReq := xml.Header + string(payload)
|
|
//
|
|
// // Crear la solicitud HTTP con el contexto
|
|
// httpReq, err := http.NewRequestWithContext(ctx, "POST", sc.soapURL, bytes.NewReader([]byte(fullReq)))
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", 0, fmt.Errorf("error creando request: %w", err)
|
|
// }
|
|
//
|
|
// // Configurar encabezados
|
|
// httpReq.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
|
// httpReq.Header.Set("Accept", "text/xml")
|
|
// httpReq.Header.Set(registro.TokenKey, registro.TokenValue)
|
|
//
|
|
// // Realizar la solicitud con timeout aumentado
|
|
// start := utils.NowInBolivia()
|
|
// client := &http.Client{
|
|
// Timeout: 120 * time.Second,
|
|
// Transport: &http.Transport{
|
|
// TLSHandshakeTimeout: 30 * time.Second,
|
|
// ResponseHeaderTimeout: 30 * time.Second,
|
|
// ExpectContinueTimeout: 10 * time.Second,
|
|
// DisableKeepAlives: false,
|
|
// IdleConnTimeout: 90 * time.Second,
|
|
// },
|
|
// }
|
|
//
|
|
// resp, err := client.Do(httpReq)
|
|
// latency := time.Since(start).Milliseconds()
|
|
//
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", latency, fmt.Errorf("error llamando al servicio: %w", err)
|
|
// }
|
|
// defer resp.Body.Close()
|
|
//
|
|
// respBytes, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, "", latency, fmt.Errorf("error leyendo respuesta: %w", err)
|
|
// }
|
|
// respStr := string(respBytes)
|
|
//
|
|
// // Parsear la respuesta
|
|
// var soapEnv struct {
|
|
// Body struct {
|
|
// Content []byte `xml:",innerxml"`
|
|
// } `xml:"Body"`
|
|
// }
|
|
//
|
|
// if err := xml.Unmarshal(respBytes, &soapEnv); err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parseando envelope: %w", err)
|
|
// }
|
|
//
|
|
// var wrap soapModels.VerificacionEstadoFacturaResponse
|
|
// if err := xml.Unmarshal(soapEnv.Body.Content, &wrap); err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parseando respuesta: %w", err)
|
|
// }
|
|
//
|
|
// return wrap.RespuestaServicioFacturacion, resp.StatusCode, fullReq, respStr, latency, nil
|
|
//}
|
|
//
|
|
//// CallAnulacionFactura sends a cancellation request to the SOAP service
|
|
//func (sc *SOAPClient) CallAnulacionFactura(
|
|
// ctx context.Context,
|
|
// registro entidad_db.DatosFactura,
|
|
// cuf string,
|
|
// codigoMotivo int,
|
|
//) (soapModels.RespuestaServicioFacturacion, int, string, string, int64, error) {
|
|
// // Build the request
|
|
// inner := soapModels.SolicitudServicioAnulacionFactura{
|
|
// CodigoAmbiente: registro.CodigoAmbiente,
|
|
// CodigoDocumentoSector: 1,
|
|
// CodigoEmision: 1,
|
|
// CodigoModalidad: registro.CodigoModalidad,
|
|
// CodigoPuntoVenta: registro.CodigoPuntoVenta,
|
|
// CodigoSistema: registro.CodigoSistema,
|
|
// CodigoSucursal: registro.CodigoSucursal,
|
|
// CUFD: registro.CUFD,
|
|
// CUIS: registro.CUIS,
|
|
// NIT: registro.NIT,
|
|
// TipoFacturaDocumento: 1,
|
|
// CodigoMotivo: codigoMotivo, // Default is 1, can be made configurable
|
|
// CUF: cuf,
|
|
// }
|
|
//
|
|
// env := soapModels.AnulacionFacturaEnvelope{
|
|
// SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
// Siat: "https://siat.impuestos.gob.bo/",
|
|
// }
|
|
// env.Body.Anulacion.Solicitud = inner
|
|
//
|
|
// payload, err := xml.MarshalIndent(env, "", " ")
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, "", "", 0, fmt.Errorf("error marshalling XML: %w", err)
|
|
// }
|
|
//
|
|
// fullReq := xml.Header + string(payload)
|
|
//
|
|
// // Create HTTP request with context
|
|
// httpReq, err := http.NewRequestWithContext(ctx, "POST", sc.soapURL, bytes.NewReader([]byte(fullReq)))
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", 0, fmt.Errorf("error creating request: %w", err)
|
|
// }
|
|
//
|
|
// // Set headers
|
|
// httpReq.Header.Set("Content-Type", "text/xml; charset=utf-8")
|
|
// httpReq.Header.Set("Accept", "text/xml")
|
|
// httpReq.Header.Set(registro.TokenKey, registro.TokenValue)
|
|
//
|
|
// // Send request with increased timeout
|
|
// start := utils.NowInBolivia()
|
|
// client := &http.Client{
|
|
// Timeout: 120 * time.Second,
|
|
// Transport: &http.Transport{
|
|
// TLSHandshakeTimeout: 30 * time.Second,
|
|
// ResponseHeaderTimeout: 30 * time.Second,
|
|
// ExpectContinueTimeout: 10 * time.Second,
|
|
// DisableKeepAlives: false,
|
|
// IdleConnTimeout: 90 * time.Second,
|
|
// },
|
|
// }
|
|
//
|
|
// resp, err := client.Do(httpReq)
|
|
// latency := time.Since(start).Milliseconds()
|
|
//
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, 0, fullReq, "", latency, fmt.Errorf("error calling service: %w", err)
|
|
// }
|
|
// defer resp.Body.Close()
|
|
//
|
|
// respBytes, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, "", latency, fmt.Errorf("error reading response: %w", err)
|
|
// }
|
|
// respStr := string(respBytes)
|
|
//
|
|
// // Parse response
|
|
// var soapEnv struct {
|
|
// Body struct {
|
|
// Content []byte `xml:",innerxml"`
|
|
// } `xml:"Body"`
|
|
// }
|
|
//
|
|
// if err := xml.Unmarshal(respBytes, &soapEnv); err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parsing envelope: %w", err)
|
|
// }
|
|
//
|
|
// var wrap soapModels.AnulacionFacturaResponse
|
|
// if err := xml.Unmarshal(soapEnv.Body.Content, &wrap); err != nil {
|
|
// return soapModels.RespuestaServicioFacturacion{}, resp.StatusCode, fullReq, respStr, latency, fmt.Errorf("error parsing response: %w", err)
|
|
// }
|
|
//
|
|
// return wrap.RespuestaServicioFacturacion, resp.StatusCode, fullReq, respStr, latency, nil
|
|
//}
|