ProyectoGrupal/VerifactuMidAPI/internal/formats/dolibarr/format.go

150 lines
3.4 KiB
Go

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"`
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"`
}
type ClientInput struct {
Name string `json:"name"`
VatNumber string `json:"vatNumber"`
}
type EmisorInput struct {
NIF string `json:"nif,omitempty"`
Nombre string `json:"nombre,omitempty"`
}
type SistemaInput struct {
Nombre string `json:"nombre,omitempty"`
NIFProveedor string `json:"nif_proveedor,omitempty"`
Version string `json:"version,omitempty"`
}
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)
}
if len(in.Invoice.Lines) == 0 {
return nil, fmt.Errorf("dolibarr format: at least one line is required")
}
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
lineTotal := line.Quantity * line.UnitPrice
base := lineTotal / (1 + rate/100)
cuota := lineTotal - 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))
var importeTotal float64
for _, v := range ivaMap {
v.Base = round2(v.Base)
v.Cuota = round2(v.Cuota)
importeTotal += v.Base + 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"
}
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
}