159 lines
4.7 KiB
Go
159 lines
4.7 KiB
Go
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"`
|
|
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"})
|
|
}
|
|
|
|
if in.Factura.Destinatario != nil {
|
|
if in.Factura.Destinatario.NIF != "" && !isValidNIF(in.Factura.Destinatario.NIF) {
|
|
errs = append(errs, &ValidationError{"factura.destinatario.nif", "invalid NIF format"})
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|