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 }