feat: add format system with auto-detection
- Add internal/formats/registry.go with Transformer interface - Add internal/formats/native/ for current API format - Add internal/formats/dolibarr/ for Dolibarr BFF format - Refactor factura.go to use TransformAuto() for auto-detection - Remove format query param — detection is now automatic
This commit is contained in:
parent
74487015df
commit
16049fa3ef
|
|
@ -1,10 +1,12 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"VerifactuMidAPI/internal/formats"
|
||||||
"VerifactuMidAPI/verifactu"
|
"VerifactuMidAPI/verifactu"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -23,11 +25,6 @@ func (s *FacturaService) SetVerifactuClient(client *verifactu.Client) {
|
||||||
s.verifactu = client
|
s.verifactu = client
|
||||||
}
|
}
|
||||||
|
|
||||||
type AltaInput struct {
|
|
||||||
InvoiceInput
|
|
||||||
EmisorNombre string
|
|
||||||
}
|
|
||||||
|
|
||||||
type AltaOutput struct {
|
type AltaOutput struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
CSV string `json:"csv,omitempty"`
|
CSV string `json:"csv,omitempty"`
|
||||||
|
|
@ -35,39 +32,81 @@ type AltaOutput struct {
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FacturaService) ProcessAlta(input AltaInput) (*AltaOutput, error) {
|
func (s *FacturaService) ProcessAlta(raw json.RawMessage) (*AltaOutput, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
log.Printf("PANIC: %v", r)
|
log.Printf("PANIC: %v", r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
errs := ValidateInvoiceInput(&input.InvoiceInput)
|
result, formatName, err := formats.TransformAuto(raw)
|
||||||
if len(errs) > 0 {
|
if err != nil {
|
||||||
errMsg := ""
|
|
||||||
for _, e := range errs {
|
|
||||||
errMsg += e.Error() + "; "
|
|
||||||
}
|
|
||||||
log.Printf("validation errors: %s", errMsg)
|
|
||||||
return &AltaOutput{
|
return &AltaOutput{
|
||||||
Success: false,
|
Success: false,
|
||||||
Error: "validation_failed: " + errMsg,
|
Error: "transform_error: " + err.Error(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := TransformToInvoiceData(&input.InvoiceInput)
|
log.Printf("detected format: %s", formatName)
|
||||||
if err != nil {
|
return s.processInvoiceData(result)
|
||||||
log.Printf("transform error: %v", err)
|
}
|
||||||
return &AltaOutput{
|
|
||||||
Success: false,
|
func (s *FacturaService) processInvoiceData(result *formats.TransformResult) (*AltaOutput, error) {
|
||||||
Error: err.Error(),
|
fecha, _ := time.Parse("02-01-2006", result.FechaExpedicion)
|
||||||
}, nil
|
|
||||||
|
ivaList := make([]IVARegularizacion, len(result.IVA))
|
||||||
|
for i, v := range result.IVA {
|
||||||
|
ivaList[i] = IVARegularizacion{
|
||||||
|
Base: v.Base,
|
||||||
|
Cuota: v.Cuota,
|
||||||
|
Tipo: v.Tipo,
|
||||||
|
ClaveRegimen: "01",
|
||||||
|
Calificacion: "S1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cuotaTotal := 0.0
|
||||||
|
for _, v := range result.IVA {
|
||||||
|
cuotaTotal += v.Cuota
|
||||||
|
}
|
||||||
|
|
||||||
|
var dest *Destinatario
|
||||||
|
if result.Destinatario != nil {
|
||||||
|
dest = &Destinatario{
|
||||||
|
Nombre: result.Destinatario.Nombre,
|
||||||
|
NIF: result.Destinatario.NIF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &InvoiceData{
|
||||||
|
Tipo: "alta",
|
||||||
|
EmisorNIF: result.EmisorNIF,
|
||||||
|
NumSerie: result.NumSerie,
|
||||||
|
Fecha: fecha,
|
||||||
|
TipoFactura: result.TipoFactura,
|
||||||
|
Descripcion: result.Descripcion,
|
||||||
|
Destinatario: dest,
|
||||||
|
IVA: ivaList,
|
||||||
|
CuotaTotal: cuotaTotal,
|
||||||
|
ImporteTotal: result.ImporteTotal,
|
||||||
|
Sistema: Sistema{
|
||||||
|
Nombre: result.Sistema.Nombre,
|
||||||
|
NIFProveedor: result.Sistema.NIFProveedor,
|
||||||
|
NombreSistema: result.Sistema.Nombre,
|
||||||
|
IDSistema: "01",
|
||||||
|
Version: result.Sistema.Version,
|
||||||
|
NumeroInstalacion: "1",
|
||||||
|
TipoUsoVerifactu: "S",
|
||||||
|
TipoUsoMultiOT: "N",
|
||||||
|
IndicadorMultiOT: "N",
|
||||||
|
},
|
||||||
|
FechaGen: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var prevHash, prevNumSerie string
|
var prevHash, prevNumSerie string
|
||||||
var prevFecha time.Time
|
var prevFecha time.Time
|
||||||
if s.hashStorage != nil {
|
if s.hashStorage != nil {
|
||||||
record, err := s.hashStorage.GetLastRecord(input.Factura.EmisorNIF)
|
record, err := s.hashStorage.GetLastRecord(result.EmisorNIF)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &AltaOutput{
|
return &AltaOutput{
|
||||||
Success: false,
|
Success: false,
|
||||||
|
|
@ -96,14 +135,38 @@ func (s *FacturaService) ProcessAlta(input AltaInput) (*AltaOutput, error) {
|
||||||
data.Huella = currentHash
|
data.Huella = currentHash
|
||||||
data.FechaGen = now
|
data.FechaGen = now
|
||||||
|
|
||||||
altaData := ToAltaData(&input.InvoiceInput, data, currentHash, prevHash, prevNumSerie, prevFecha)
|
altaData := verifactu.AltaData{
|
||||||
|
EmisorNombre: result.EmisorNombre,
|
||||||
|
EmisorNIF: data.EmisorNIF,
|
||||||
|
NumSerie: data.NumSerie,
|
||||||
|
FechaExpedicion: data.Fecha,
|
||||||
|
TipoFactura: data.TipoFactura,
|
||||||
|
Descripcion: data.Descripcion,
|
||||||
|
DestinatarioNombre: destNombre(data.Destinatario),
|
||||||
|
DestinatarioNIF: destNIF(data.Destinatario),
|
||||||
|
IVA: toIVAData(data.IVA),
|
||||||
|
CuotaTotal: data.CuotaTotal,
|
||||||
|
ImporteTotal: data.ImporteTotal,
|
||||||
|
Sistema: verifactu.SistemaData{
|
||||||
|
Nombre: data.Sistema.Nombre,
|
||||||
|
NIFProveedor: data.Sistema.NIFProveedor,
|
||||||
|
NombreSistema: data.Sistema.NombreSistema,
|
||||||
|
Version: data.Sistema.Version,
|
||||||
|
NumeroInstalacion: data.Sistema.NumeroInstalacion,
|
||||||
|
TipoUsoVerifactu: data.Sistema.TipoUsoVerifactu,
|
||||||
|
},
|
||||||
|
Huella: currentHash,
|
||||||
|
PrevHash: prevHash,
|
||||||
|
PrevNumSerie: prevNumSerie,
|
||||||
|
PrevFecha: prevFecha,
|
||||||
|
FechaGen: data.FechaGen,
|
||||||
|
}
|
||||||
|
|
||||||
if s.verifactu != nil {
|
if s.verifactu != nil {
|
||||||
log.Printf("Sending to AEAT...")
|
log.Printf("Sending to AEAT...")
|
||||||
resp, err := s.verifactu.SendAlta(altaData)
|
resp, err := s.verifactu.SendAlta(altaData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("AEAT error: %v", err)
|
log.Printf("AEAT error: %v", err)
|
||||||
log.Printf("AEAT error, falling back to local: %v", err)
|
|
||||||
goto saveLocal
|
goto saveLocal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,6 +226,34 @@ saveLocal:
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func destNombre(d *Destinatario) string {
|
||||||
|
if d == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return d.Nombre
|
||||||
|
}
|
||||||
|
|
||||||
|
func destNIF(d *Destinatario) string {
|
||||||
|
if d == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return d.NIF
|
||||||
|
}
|
||||||
|
|
||||||
|
func toIVAData(list []IVARegularizacion) []verifactu.IVARegularizacionData {
|
||||||
|
out := make([]verifactu.IVARegularizacionData, len(list))
|
||||||
|
for i, v := range list {
|
||||||
|
out[i] = verifactu.IVARegularizacionData{
|
||||||
|
Base: v.Base,
|
||||||
|
Cuota: v.Cuota,
|
||||||
|
Tipo: v.Tipo,
|
||||||
|
ClaveRegimen: v.ClaveRegimen,
|
||||||
|
Calificacion: v.Calificacion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
type AnulacionInput struct {
|
type AnulacionInput struct {
|
||||||
InvoiceInput
|
InvoiceInput
|
||||||
EmisorNombre string
|
EmisorNombre string
|
||||||
|
|
@ -200,50 +291,3 @@ func (s *FacturaService) ProcessAnulacion(input AnulacionInput) (*AnulacionOutpu
|
||||||
Estado: "Anulada",
|
Estado: "Anulada",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToAltaData(in *InvoiceInput, data *InvoiceData, huella, prevHash, prevNumSerie string, prevFecha time.Time) verifactu.AltaData {
|
|
||||||
ivaData := make([]verifactu.IVARegularizacionData, len(data.IVA))
|
|
||||||
for i, iva := range data.IVA {
|
|
||||||
ivaData[i] = verifactu.IVARegularizacionData{
|
|
||||||
Base: iva.Base,
|
|
||||||
Cuota: iva.Cuota,
|
|
||||||
Tipo: iva.Tipo,
|
|
||||||
ClaveRegimen: iva.ClaveRegimen,
|
|
||||||
Calificacion: iva.Calificacion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
destNombre := ""
|
|
||||||
destNIF := ""
|
|
||||||
if data.Destinatario != nil {
|
|
||||||
destNombre = data.Destinatario.Nombre
|
|
||||||
destNIF = data.Destinatario.NIF
|
|
||||||
}
|
|
||||||
|
|
||||||
return verifactu.AltaData{
|
|
||||||
EmisorNombre: in.Factura.EmisorNombre,
|
|
||||||
EmisorNIF: data.EmisorNIF,
|
|
||||||
NumSerie: data.NumSerie,
|
|
||||||
FechaExpedicion: data.Fecha,
|
|
||||||
TipoFactura: data.TipoFactura,
|
|
||||||
Descripcion: data.Descripcion,
|
|
||||||
DestinatarioNombre: destNombre,
|
|
||||||
DestinatarioNIF: destNIF,
|
|
||||||
IVA: ivaData,
|
|
||||||
CuotaTotal: data.CuotaTotal,
|
|
||||||
ImporteTotal: data.ImporteTotal,
|
|
||||||
Sistema: verifactu.SistemaData{
|
|
||||||
Nombre: data.Sistema.Nombre,
|
|
||||||
NIFProveedor: data.Sistema.NIFProveedor,
|
|
||||||
NombreSistema: data.Sistema.NombreSistema,
|
|
||||||
Version: data.Sistema.Version,
|
|
||||||
NumeroInstalacion: data.Sistema.NumeroInstalacion,
|
|
||||||
TipoUsoVerifactu: data.Sistema.TipoUsoVerifactu,
|
|
||||||
},
|
|
||||||
Huella: huella,
|
|
||||||
PrevHash: prevHash,
|
|
||||||
PrevNumSerie: prevNumSerie,
|
|
||||||
PrevFecha: prevFecha,
|
|
||||||
FechaGen: data.FechaGen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
package dolibarr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"VerifactuMidAPI/internal/formats"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
formats.Register(&Transformer{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transformer struct{}
|
||||||
|
|
||||||
|
func (t *Transformer) Name() string { return "dolibarr" }
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
Invoice InvoiceInput `json:"invoice"`
|
||||||
|
Client *ClientInput `json:"client,omitempty"`
|
||||||
|
Emisor EmisorInput `json:"emisor"`
|
||||||
|
Sistema SistemaInput `json:"sistema"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InvoiceInput struct {
|
||||||
|
Number string `json:"number"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
TotalHt float64 `json:"totalHt"`
|
||||||
|
TotalTax float64 `json:"totalTax"`
|
||||||
|
Total float64 `json:"total"`
|
||||||
|
NotePublic string `json:"notePublic,omitempty"`
|
||||||
|
Lines []LineInput `json:"lines"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LineInput struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Quantity float64 `json:"quantity"`
|
||||||
|
UnitPrice float64 `json:"unitPrice"`
|
||||||
|
TaxRate float64 `json:"taxRate"`
|
||||||
|
Total float64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientInput struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
VatNumber string `json:"vatNumber"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmisorInput struct {
|
||||||
|
NIF string `json:"nif"`
|
||||||
|
Nombre string `json:"nombre"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SistemaInput struct {
|
||||||
|
Nombre string `json:"nombre"`
|
||||||
|
NIFProveedor string `json:"nif_proveedor"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transformer) Transform(raw json.RawMessage) (*formats.TransformResult, error) {
|
||||||
|
var in Input
|
||||||
|
if err := json.Unmarshal(raw, &in); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid dolibarr format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
date, err := parseDate(in.Invoice.Date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid invoice date: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ivaMap := make(map[float64]*formats.IVAData)
|
||||||
|
for _, line := range in.Invoice.Lines {
|
||||||
|
rate := line.TaxRate
|
||||||
|
base := line.Total / (1 + rate/100)
|
||||||
|
cuota := line.Total - base
|
||||||
|
|
||||||
|
if existing, ok := ivaMap[rate]; ok {
|
||||||
|
existing.Base += base
|
||||||
|
existing.Cuota += cuota
|
||||||
|
} else {
|
||||||
|
ivaMap[rate] = &formats.IVAData{
|
||||||
|
Base: base,
|
||||||
|
Cuota: cuota,
|
||||||
|
Tipo: rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iva := make([]formats.IVAData, 0, len(ivaMap))
|
||||||
|
for _, v := range ivaMap {
|
||||||
|
v.Base = round2(v.Base)
|
||||||
|
v.Cuota = round2(v.Cuota)
|
||||||
|
iva = append(iva, *v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dest *formats.DestinatarioData
|
||||||
|
if in.Client != nil && in.Client.VatNumber != "" {
|
||||||
|
dest = &formats.DestinatarioData{
|
||||||
|
Nombre: in.Client.Name,
|
||||||
|
NIF: in.Client.VatNumber,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
desc := in.Invoice.NotePublic
|
||||||
|
if desc == "" {
|
||||||
|
desc = "Factura"
|
||||||
|
}
|
||||||
|
|
||||||
|
importeTotal := in.Invoice.Total
|
||||||
|
if importeTotal == 0 {
|
||||||
|
importeTotal = in.Invoice.TotalHt + in.Invoice.TotalTax
|
||||||
|
}
|
||||||
|
|
||||||
|
return &formats.TransformResult{
|
||||||
|
EmisorNIF: in.Emisor.NIF,
|
||||||
|
EmisorNombre: in.Emisor.Nombre,
|
||||||
|
NumSerie: in.Invoice.Number,
|
||||||
|
FechaExpedicion: date,
|
||||||
|
TipoFactura: "F1",
|
||||||
|
Descripcion: desc,
|
||||||
|
Destinatario: dest,
|
||||||
|
IVA: iva,
|
||||||
|
ImporteTotal: round2(importeTotal),
|
||||||
|
Sistema: formats.SistemaData{
|
||||||
|
Nombre: in.Sistema.Nombre,
|
||||||
|
NIFProveedor: in.Sistema.NIFProveedor,
|
||||||
|
Version: in.Sistema.Version,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDate(s string) (string, error) {
|
||||||
|
formats := []string{
|
||||||
|
"2006-01-02T15:04:05Z07:00",
|
||||||
|
"2006-01-02T15:04:05Z",
|
||||||
|
"2006-01-02T15:04:05",
|
||||||
|
"2006-01-02",
|
||||||
|
}
|
||||||
|
for _, f := range formats {
|
||||||
|
t, err := time.Parse(f, s)
|
||||||
|
if err == nil {
|
||||||
|
return t.Format("02-01-2006"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cannot parse date %q", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func round2(v float64) float64 {
|
||||||
|
return math.Round(v*100) / 100
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"VerifactuMidAPI/internal/formats"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
formats.Register(&Transformer{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transformer struct{}
|
||||||
|
|
||||||
|
func (t *Transformer) Name() string { return "native" }
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
Tipo string `json:"tipo"`
|
||||||
|
Factura FacturaInput `json:"factura"`
|
||||||
|
Sistema SistemaInput `json:"sistema"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FacturaInput struct {
|
||||||
|
EmisorNIF string `json:"emisor_nif"`
|
||||||
|
EmisorNombre string `json:"emisor_nombre"`
|
||||||
|
NumSerie string `json:"num_serie"`
|
||||||
|
FechaExpedicion string `json:"fecha_expedicion"`
|
||||||
|
TipoFactura string `json:"tipo_factura"`
|
||||||
|
Descripcion string `json:"descripcion"`
|
||||||
|
Destinatario *DestinatarioInput `json:"destinatario,omitempty"`
|
||||||
|
IVA []IVAInput `json:"iva"`
|
||||||
|
ImporteTotal float64 `json:"importe_total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DestinatarioInput struct {
|
||||||
|
Nombre string `json:"nombre"`
|
||||||
|
NIF string `json:"nif"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVAInput struct {
|
||||||
|
Base float64 `json:"base"`
|
||||||
|
Cuota float64 `json:"cuota"`
|
||||||
|
Tipo float64 `json:"tipo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SistemaInput struct {
|
||||||
|
Nombre string `json:"nombre"`
|
||||||
|
NIFProveedor string `json:"nif_proveedor"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transformer) Transform(raw json.RawMessage) (*formats.TransformResult, error) {
|
||||||
|
var in Input
|
||||||
|
if err := json.Unmarshal(raw, &in); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid native format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dest *formats.DestinatarioData
|
||||||
|
if in.Factura.Destinatario != nil {
|
||||||
|
dest = &formats.DestinatarioData{
|
||||||
|
Nombre: in.Factura.Destinatario.Nombre,
|
||||||
|
NIF: in.Factura.Destinatario.NIF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iva := make([]formats.IVAData, len(in.Factura.IVA))
|
||||||
|
for i, v := range in.Factura.IVA {
|
||||||
|
iva[i] = formats.IVAData{Base: v.Base, Cuota: v.Cuota, Tipo: v.Tipo}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := time.Parse("02-01-2006", in.Factura.FechaExpedicion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid fecha_expedicion: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &formats.TransformResult{
|
||||||
|
EmisorNIF: in.Factura.EmisorNIF,
|
||||||
|
EmisorNombre: in.Factura.EmisorNombre,
|
||||||
|
NumSerie: in.Factura.NumSerie,
|
||||||
|
FechaExpedicion: in.Factura.FechaExpedicion,
|
||||||
|
TipoFactura: in.Factura.TipoFactura,
|
||||||
|
Descripcion: in.Factura.Descripcion,
|
||||||
|
Destinatario: dest,
|
||||||
|
IVA: iva,
|
||||||
|
ImporteTotal: in.Factura.ImporteTotal,
|
||||||
|
Sistema: formats.SistemaData{
|
||||||
|
Nombre: in.Sistema.Nombre,
|
||||||
|
NIFProveedor: in.Sistema.NIFProveedor,
|
||||||
|
Version: in.Sistema.Version,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package formats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Transformer interface {
|
||||||
|
Name() string
|
||||||
|
Transform(raw json.RawMessage) (*TransformResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransformResult struct {
|
||||||
|
EmisorNIF string
|
||||||
|
EmisorNombre string
|
||||||
|
NumSerie string
|
||||||
|
FechaExpedicion string
|
||||||
|
TipoFactura string
|
||||||
|
Descripcion string
|
||||||
|
Destinatario *DestinatarioData
|
||||||
|
IVA []IVAData
|
||||||
|
ImporteTotal float64
|
||||||
|
Sistema SistemaData
|
||||||
|
}
|
||||||
|
|
||||||
|
type DestinatarioData struct {
|
||||||
|
Nombre string
|
||||||
|
NIF string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IVAData struct {
|
||||||
|
Base float64
|
||||||
|
Cuota float64
|
||||||
|
Tipo float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SistemaData struct {
|
||||||
|
Nombre string
|
||||||
|
NIFProveedor string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
var registry = make(map[string]Transformer)
|
||||||
|
var order []string
|
||||||
|
|
||||||
|
func Register(t Transformer) {
|
||||||
|
name := t.Name()
|
||||||
|
if _, exists := registry[name]; !exists {
|
||||||
|
order = append(order, name)
|
||||||
|
}
|
||||||
|
registry[name] = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func TransformAuto(raw json.RawMessage) (*TransformResult, string, error) {
|
||||||
|
for _, name := range order {
|
||||||
|
t := registry[name]
|
||||||
|
result, err := t.Transform(raw)
|
||||||
|
if err == nil {
|
||||||
|
return result, name, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, "", fmt.Errorf("no matching format found (available: %v)", Available())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Available() []string {
|
||||||
|
names := make([]string, len(order))
|
||||||
|
copy(names, order)
|
||||||
|
sort.Strings(names)
|
||||||
|
return names
|
||||||
|
}
|
||||||
2
main.go
2
main.go
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"VerifactuMidAPI/api"
|
"VerifactuMidAPI/api"
|
||||||
|
_ "VerifactuMidAPI/internal/formats/dolibarr"
|
||||||
|
_ "VerifactuMidAPI/internal/formats/native"
|
||||||
"VerifactuMidAPI/internal"
|
"VerifactuMidAPI/internal"
|
||||||
"VerifactuMidAPI/internal/cert"
|
"VerifactuMidAPI/internal/cert"
|
||||||
"VerifactuMidAPI/internal/config"
|
"VerifactuMidAPI/internal/config"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue