Skip to content

Commit

Permalink
Defining common invoice tags, simplified invoice validation moved up
Browse files Browse the repository at this point in the history
  • Loading branch information
samlown committed Nov 20, 2023
1 parent 8787541 commit f082f48
Show file tree
Hide file tree
Showing 20 changed files with 136 additions and 145 deletions.
48 changes: 31 additions & 17 deletions bill/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,39 +114,53 @@ func (inv *Invoice) ValidateWithContext(ctx context.Context) error {
ctx = r.WithContext(ctx)
err := validation.ValidateStructWithContext(ctx, inv,
validation.Field(&inv.UUID),
validation.Field(&inv.Type, validation.Required, isValidInvoiceType),
validation.Field(&inv.Type,
validation.Required,
isValidInvoiceType,
),
validation.Field(&inv.Series),
validation.Field(&inv.Code,
validation.When(!internal.IsDraft(ctx), validation.Required),
validation.When(
!internal.IsDraft(ctx),
validation.Required,
),
),
validation.Field(&inv.IssueDate,
cal.DateNotZero(),
),
validation.Field(&inv.IssueDate, cal.DateNotZero()),
validation.Field(&inv.OperationDate),
validation.Field(&inv.ValueDate),
validation.Field(&inv.Currency, validation.Required),
validation.Field(&inv.Currency,
validation.Required,
),
validation.Field(&inv.ExchangeRates),

validation.Field(&inv.Preceding),

validation.Field(&inv.Tax),

validation.Field(&inv.Supplier, validation.Required),
validation.Field(&inv.Customer),

validation.Field(&inv.Lines, validation.Required),
validation.Field(&inv.Supplier,
validation.Required,
),
validation.Field(&inv.Customer,
// Customer is not required for simplified invoices.
validation.When(
!inv.Tax.ContainsTag(common.TagSimplified),
validation.Required,
),
),
validation.Field(&inv.Lines,
validation.Required,
),
validation.Field(&inv.Discounts),
validation.Field(&inv.Charges),
validation.Field(&inv.Outlays),

validation.Field(&inv.Ordering),
validation.Field(&inv.Payment),
validation.Field(&inv.Delivery),

validation.Field(&inv.Totals, validation.Required),

validation.Field(&inv.Totals,
validation.Required,
),
validation.Field(&inv.Notes),
validation.Field(&inv.Complements),
validation.Field(&inv.Meta),

validation.Field(&inv.Complements, validation.Each()),
)
if err == nil {
err = r.ValidateObject(inv)
Expand Down
3 changes: 2 additions & 1 deletion regimes/co/co.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/invopop/gobl/l10n"
"github.com/invopop/gobl/org"
"github.com/invopop/gobl/pkg/here"
"github.com/invopop/gobl/regimes/common"
"github.com/invopop/gobl/tax"
)

Expand Down Expand Up @@ -44,7 +45,7 @@ func New() *tax.Regime {
`),
},
TimeZone: "America/Bogota",
Tags: invoiceTags,
Tags: common.InvoiceTags(),
Validator: Validate,
Calculator: Calculate,
IdentityTypeKeys: taxIdentityTypeDefs, // see tax_identity.go
Expand Down
23 changes: 0 additions & 23 deletions regimes/co/invoices.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,12 @@ package co
import (
"github.com/invopop/gobl/bill"
"github.com/invopop/gobl/currency"
"github.com/invopop/gobl/i18n"
"github.com/invopop/gobl/l10n"
"github.com/invopop/gobl/org"
"github.com/invopop/gobl/regimes/common"
"github.com/invopop/gobl/tax"
"github.com/invopop/validation"
)

var invoiceTags = []*tax.KeyDefinition{
// Simplified invoices when a client ID is not available. For conversion to local formats,
// the correct client ID data will need to be provided automatically.
{
Key: common.TagSimplified,
Name: i18n.String{
i18n.EN: "Simplified Invoice",
i18n.ES: "Factura Simplificada",
},
Desc: i18n.String{
i18n.EN: "Used for B2C transactions when the client details are not available, check with local authorities for limits.",
i18n.ES: "Usado para transacciones B2C cuando los detalles del cliente no están disponibles, consulte con las autoridades locales para los límites.",
},
},
}

type invoiceValidator struct {
inv *bill.Invoice
}
Expand All @@ -48,15 +30,10 @@ func (v *invoiceValidator) validate() error {
),
),
validation.Field(&inv.Supplier,
validation.Required,
validation.By(v.validParty),
validation.By(v.validSupplier),
),
validation.Field(&inv.Customer,
validation.When(
!inv.Tax.ContainsTag(common.TagSimplified),
validation.Required,
),
validation.By(v.validParty),
),
validation.Field(&inv.Preceding,
Expand Down
69 changes: 69 additions & 0 deletions regimes/common/invoices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package common

import (
"github.com/invopop/gobl/i18n"
"github.com/invopop/gobl/tax"
)

var invoiceTags = []*tax.KeyDefinition{
// Simplified invoices are issued when the complete fiscal details of
// a customer are not available.
{
Key: TagSimplified,
Name: i18n.String{
i18n.EN: "Simplified Invoice",
i18n.ES: "Factura Simplificada",
i18n.IT: "Fattura Semplificata",
},
Desc: i18n.String{
i18n.EN: "Used for B2C transactions when the client details are not available, check with local authorities for limits.",
i18n.ES: "Usado para transacciones B2C cuando los detalles del cliente no están disponibles, consulte con las autoridades locales para los límites.",
i18n.IT: "Utilizzato per le transazioni B2C quando i dettagli del cliente non sono disponibili, controllare con le autorità locali per i limiti.",
},
},

// Reverse Charge mechanism is used when the supplier is not
// required to charge VAT on the invoice and the customer is
// responsible for paying the VAT to the tax authorities.
{
Key: TagReverseCharge,
Name: i18n.String{
i18n.EN: "Reverse Charge",
i18n.ES: "Inversión del Sujeto Pasivo",
i18n.IT: "Inversione del soggetto passivo",
},
},

// Self-billed invoices are issued by the customer instead of the
// supplier. This is usually done when the customer is a large
// company and the supplier is a small company.
{
Key: TagSelfBilled,
Name: i18n.String{
i18n.EN: "Self-billed",
i18n.ES: "Facturación por el destinatario",
i18n.IT: "Autofattura",
},
},

// Customer rates (mainly for digital goods inside EU)
{
Key: TagCustomerRates,
Name: i18n.String{
i18n.EN: "Customer rates",
i18n.ES: "Tarifas aplicables al destinatario",
},
},
}

// InvoiceTags returns a list of common invoice tag key
// definitions.
func InvoiceTags() []*tax.KeyDefinition {
return invoiceTags
}

// InvoiceTagsWith appends the list of provided key definitions
// to the base list of tags and returns a new array.
func InvoiceTagsWith(tags []*tax.KeyDefinition) []*tax.KeyDefinition {
return append(InvoiceTags(), tags...)
}
7 changes: 1 addition & 6 deletions regimes/es/invoices.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"github.com/invopop/gobl/currency"
"github.com/invopop/gobl/l10n"
"github.com/invopop/gobl/org"
"github.com/invopop/gobl/regimes/common"
"github.com/invopop/gobl/tax"
"github.com/invopop/validation"
)
Expand Down Expand Up @@ -40,11 +39,7 @@ func (v *invoiceValidator) validate() error {
validation.By(v.supplier),
),
validation.Field(&inv.Customer,
validation.When(
!inv.Tax.ContainsTag(common.TagSimplified),
validation.Required,
validation.By(v.commercialCustomer),
),
validation.By(v.commercialCustomer),
),
)
}
Expand Down
36 changes: 2 additions & 34 deletions regimes/es/scenarios.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,7 @@ const (
TagCashBasis cbc.Key = "cash-basis"
)

var invoiceTags = []*tax.KeyDefinition{
// Simplified Invoice
{
Key: common.TagSimplified,
Name: i18n.String{
i18n.EN: "Simplified Invoice",
i18n.ES: "Factura Simplificada",
},
},
// Customer rates (mainly for digital goods inside EU)
{
Key: common.TagCustomerRates,
Name: i18n.String{
i18n.EN: "Customer rates",
i18n.ES: "Tarifas aplicables al destinatario",
},
},
// Reverse Charge Mechanism
{
Key: common.TagReverseCharge,
Name: i18n.String{
i18n.EN: "Reverse Charge",
i18n.ES: "Inversión del sujeto pasivo",
},
},
// Customer issued invoices
{
Key: common.TagSelfBilled,
Name: i18n.String{
i18n.EN: "Customer issued invoice",
i18n.ES: "Facturación por el destinatario",
},
},
var invoiceTags = common.InvoiceTagsWith([]*tax.KeyDefinition{
// Copy of the original document
{
Key: TagCopy,
Expand Down Expand Up @@ -119,7 +87,7 @@ var invoiceTags = []*tax.KeyDefinition{
i18n.ES: "Régimen especial del criterio de caja",
},
},
}
})

var invoiceScenarios = &tax.ScenarioSet{
Schema: bill.ShortSchemaInvoice,
Expand Down
3 changes: 2 additions & 1 deletion regimes/fr/fr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/invopop/gobl/i18n"
"github.com/invopop/gobl/l10n"
"github.com/invopop/gobl/pkg/here"
"github.com/invopop/gobl/regimes/common"
"github.com/invopop/gobl/tax"
)

Expand Down Expand Up @@ -39,7 +40,7 @@ func New() *tax.Regime {
`),
},
TimeZone: "Europe/Paris",
Tags: invoiceTags,
Tags: common.InvoiceTags(),
Scenarios: []*tax.ScenarioSet{
invoiceScenarios,
},
Expand Down
12 changes: 0 additions & 12 deletions regimes/fr/scenarios.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,10 @@ package fr
import (
"github.com/invopop/gobl/bill"
"github.com/invopop/gobl/cbc"
"github.com/invopop/gobl/i18n"
"github.com/invopop/gobl/regimes/common"
"github.com/invopop/gobl/tax"
)

var invoiceTags = []*tax.KeyDefinition{
// Reverse Charge Mechanism
{
Key: common.TagReverseCharge,
Name: i18n.String{
i18n.EN: "Reverse Charge",
i18n.ES: "Inversión del sujeto pasivo",
},
},
}

var invoiceScenarios = &tax.ScenarioSet{
Schema: bill.ShortSchemaInvoice,
List: []*tax.Scenario{
Expand Down
1 change: 1 addition & 0 deletions regimes/gb/gb.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func New() *tax.Regime {
TimeZone: "Europe/London",
Validator: Validate,
Calculator: Calculate,
Tags: common.InvoiceTags(),
Categories: taxCategories,
}
}
Expand Down
10 changes: 3 additions & 7 deletions regimes/gb/invoice_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package gb
import (
"github.com/invopop/gobl/bill"
"github.com/invopop/gobl/currency"
"github.com/invopop/gobl/regimes/common"
"github.com/invopop/validation"
)

Expand All @@ -21,11 +20,8 @@ func validateInvoice(inv *bill.Invoice) error {
func (v *invoiceValidator) validate() error {
inv := v.inv
return validation.ValidateStruct(inv,
validation.Field(&inv.Currency, validation.In(currency.GBP)),
validation.Field(&inv.Supplier, validation.Required),
validation.Field(&inv.Customer, validation.When(
!inv.Tax.ContainsTag(common.TagSimplified),
validation.Required,
)),
validation.Field(&inv.Currency,
validation.In(currency.GBP),
),
)
}
14 changes: 10 additions & 4 deletions regimes/it/invoice_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ func (v *invoiceValidator) validate() error {

inv := v.inv
return validation.ValidateStruct(inv,
validation.Field(&inv.Currency, validation.In(currency.EUR)),
validation.Field(&inv.Supplier, validation.By(v.supplier)),
validation.Field(&inv.Customer, validation.By(v.customer)),
validation.Field(&inv.Currency,
validation.In(currency.EUR),
),
validation.Field(&inv.Supplier,
validation.By(v.supplier),
),
validation.Field(&inv.Customer,
validation.By(v.customer),
),
)
}

Expand Down Expand Up @@ -59,7 +65,7 @@ func (v *invoiceValidator) supplier(value interface{}) error {
func (v *invoiceValidator) customer(value interface{}) error {
customer, _ := value.(*org.Party)
if customer == nil {
return errors.New("missing customer details")
return nil
}

// Customers must have a tax ID (PartitaIVA) if they are Italian legal entities
Expand Down
Loading

0 comments on commit f082f48

Please sign in to comment.