VerifactuMidAPI/internal/models.go

157 lines
4.6 KiB
Go
Raw Normal View History

package internal
import (
"errors"
"fmt"
"regexp"
"strconv"
"time"
)
var (
ErrEmptyNIF = errors.New("NIF cannot be empty")
ErrInvalidNIF = errors.New("invalid NIF format")
ErrEmptyNumSerie = errors.New("number series cannot be empty")
ErrInvalidFecha = errors.New("invalid date format, expected dd-mm-yyyy")
ErrInvalidTipo = errors.New("invalid invoice type")
ErrInvalidImporte = errors.New("invalid amount format")
ErrEmptyNombre = errors.New("business name cannot be empty")
ErrInvalidIVACampo = errors.New("invalid IVA field: must be numeric")
)
var nifRegex = regexp.MustCompile(`^[A-Z0-9]\d{7}[A-Z]$`)
var tipoFacturaValidos = map[string]bool{
"F1": true, "F2": true,
"R1": true, "R2": true, "R3": true, "R4": true, "R5": true,
}
type InvoiceInput 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"`
}
type ValidationError struct {
Field string `json:"field"`
Message string `json:"message"`
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func ValidateInvoiceInput(in *InvoiceInput) []error {
var errs []error
if in.Tipo == "" {
errs = append(errs, &ValidationError{"tipo", "operation type is required"})
} else if in.Tipo != "alta" && in.Tipo != "anulacion" {
errs = append(errs, &ValidationError{"tipo", "must be 'alta' or 'anulacion'"})
}
if in.Factura.EmisorNIF == "" {
errs = append(errs, &ValidationError{"factura.emisor_nif", "cannot be empty"})
} else if !isValidNIF(in.Factura.EmisorNIF) {
errs = append(errs, &ValidationError{"factura.emisor_nif", "invalid NIF format"})
}
if in.Factura.NumSerie == "" {
errs = append(errs, &ValidationError{"factura.num_serie", "cannot be empty"})
}
if in.Factura.FechaExpedicion == "" {
errs = append(errs, &ValidationError{"factura.fecha_expedicion", "cannot be empty"})
} else if !isValidDate(in.Factura.FechaExpedicion) {
errs = append(errs, &ValidationError{"factura.fecha_expedicion", "invalid format, expected dd-mm-yyyy"})
}
if in.Factura.TipoFactura == "" {
errs = append(errs, &ValidationError{"factura.tipo_factura", "cannot be empty"})
} else if !tipoFacturaValidos[in.Factura.TipoFactura] {
errs = append(errs, &ValidationError{"factura.tipo_factura", "invalid invoice type"})
}
if len(in.Factura.IVA) == 0 {
errs = append(errs, &ValidationError{"factura.iva", "at least one IVA entry is required"})
}
for i, iva := range in.Factura.IVA {
if iva.Base < 0 {
errs = append(errs, &ValidationError{fmt.Sprintf("factura.iva[%d].base", i), "must be positive"})
}
if iva.Cuota < 0 {
errs = append(errs, &ValidationError{fmt.Sprintf("factura.iva[%d].cuota", i), "must be positive"})
}
if iva.Tipo < 0 {
errs = append(errs, &ValidationError{fmt.Sprintf("factura.iva[%d].tipo", i), "must be positive"})
}
}
if in.Factura.ImporteTotal <= 0 {
errs = append(errs, &ValidationError{"factura.importe_total", "must be greater than 0"})
}
// Destinatario es opcional, no se valida NIF
_ = in.Factura.Destinatario
if in.Sistema.Nombre == "" {
errs = append(errs, &ValidationError{"sistema.nombre", "cannot be empty"})
}
if in.Sistema.NIFProveedor == "" {
errs = append(errs, &ValidationError{"sistema.nif_proveedor", "cannot be empty"})
}
if in.Sistema.Version == "" {
errs = append(errs, &ValidationError{"sistema.version", "cannot be empty"})
}
return errs
}
func isValidNIF(nif string) bool {
if len(nif) != 9 {
return false
}
return nifRegex.MatchString(nif)
}
func isValidDate(date string) bool {
_, err := time.Parse("02-01-2006", date)
return err == nil
}
func ParseFloat(s string) (float64, error) {
if s == "" {
return 0, ErrInvalidImporte
}
return strconv.ParseFloat(s, 64)
}