150 lines
3.3 KiB
Go
150 lines
3.3 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"`
|
|
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)
|
|
}
|
|
|
|
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
|
|
}
|