Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validating all regimes, with scenario tag validation #225

Merged
merged 2 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions regimes/co/corrections.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ var correctionMethodList = []*tax.KeyDefinition{
},
{
Key: CorrectionMethodKeyOther,
Desc: i18n.String{
i18n.EN: "Otros.",
i18n.ES: "Other.",
Name: i18n.String{
i18n.EN: "Other",
i18n.ES: "Otros",
},
Map: cbc.CodeMap{
KeyDIAN: "5",
Expand Down
13 changes: 13 additions & 0 deletions regimes/common/invoice_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ var invoiceTags = []*tax.KeyDefinition{
i18n.DE: "Kundensätze",
},
},

// Partial invoice document, implying that this is only a first part
// and a final invoice for the remaining amount will be made later.
// A few regimes use this tag to classify invoices, notably Italy.
{
Key: tax.TagPartial,
Name: i18n.String{
i18n.EN: "Partial",
i18n.ES: "Parcial",
i18n.IT: "Parziale",
i18n.DE: "Teilweise",
},
},
}

// InvoiceTags returns a list of common invoice tag key
Expand Down
6 changes: 5 additions & 1 deletion regimes/pt/tax_categories.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,11 @@ var taxCategories = []*tax.Category{
},
},
{
Key: tax.RateExempt,
Key: tax.RateExempt,
Name: i18n.String{
i18n.EN: "Exempt",
i18n.PT: "Isento",
},
Exempt: true,
Map: cbc.CodeMap{
KeyATTaxCode: TaxCodeExempt,
Expand Down
30 changes: 22 additions & 8 deletions tax/regime.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,14 @@ func (r *Regime) CorrectionDefinitionFor(schema string) *CorrectionDefinition {
// Validate enures the region definition is valid, including all
// subsequent categories.
func (r *Regime) Validate() error {
err := validation.ValidateStruct(r,
return r.ValidateWithContext(context.Background())
}

// ValidateWithContext enures the region definition is valid, including all
// subsequent categories, and passes through the context.
func (r *Regime) ValidateWithContext(ctx context.Context) error {
ctx = context.WithValue(ctx, KeyRegime, r)
err := validation.ValidateStructWithContext(ctx, r,
validation.Field(&r.Country, validation.Required),
validation.Field(&r.Name, validation.Required),
validation.Field(&r.Description),
Expand Down Expand Up @@ -466,9 +473,9 @@ func ValidateStructWithRegime(ctx context.Context, obj interface{}, fields ...*v
return ValidateInRegime(ctx, obj)
}

// Validate ensures the Category's contents are correct.
func (c *Category) Validate() error {
err := validation.ValidateStruct(c,
// ValidateWithContext ensures the Category's contents are correct.
func (c *Category) ValidateWithContext(ctx context.Context) error {
err := validation.ValidateStructWithContext(ctx, c,
validation.Field(&c.Code, validation.Required),
validation.Field(&c.Name, validation.Required),
validation.Field(&c.Title, validation.Required),
Expand Down Expand Up @@ -500,17 +507,20 @@ func (s *Source) Validate() error {
)
}

// Validate checks that our tax definition is valid. This is only really
// ValidateWithContext checks that our tax definition is valid. This is only really
// meant to be used when testing new regional tax definitions.
func (r *Rate) Validate() error {
err := validation.ValidateStruct(r,
func (r *Rate) ValidateWithContext(ctx context.Context) error {
reg := ctx.Value(KeyRegime).(*Regime)
err := validation.ValidateStructWithContext(ctx, r,
validation.Field(&r.Key, validation.Required),
validation.Field(&r.Name, validation.Required),
validation.Field(&r.Values,
validation.When(r.Exempt, validation.Nil),
validation.By(checkRateValuesOrder),
),
validation.Field(&r.Extensions),
validation.Field(&r.Extensions,
validation.Each(InKeyDefs(reg.Extensions)),
),
validation.Field(&r.Map),
validation.Field(&r.Meta),
)
Expand Down Expand Up @@ -542,6 +552,10 @@ func checkRateValuesOrder(list interface{}) error {
// loop through and check order of Since value
for i := range values {
v := values[i]
if len(v.Zones) > 0 {
// TODO: check zone order also
continue
}
if date != nil && date.IsValid() {
if v.Since.IsValid() && !v.Since.Before(date.Date) {
return errors.New("invalid date order")
Expand Down
16 changes: 16 additions & 0 deletions tax/regimes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package tax_test

import (
"testing"

"github.com/invopop/gobl/tax"
"github.com/stretchr/testify/assert"
)

func TestAllRegimes(t *testing.T) {
for _, r := range tax.AllRegimes() {
t.Run(r.Name.String(), func(t *testing.T) {
assert.NoError(t, r.Validate())
})
}
}
18 changes: 11 additions & 7 deletions tax/scenario.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package tax

import (
"context"

"github.com/invopop/gobl/cbc"
"github.com/invopop/gobl/i18n"
"github.com/invopop/validation"
Expand Down Expand Up @@ -47,9 +49,9 @@ type ScenarioSummary struct {
Meta cbc.Meta
}

// Validate checks the scenario set for errors.
func (ss *ScenarioSet) Validate() error {
err := validation.ValidateStruct(ss,
// ValidateWithContext checks the scenario set for errors.
func (ss *ScenarioSet) ValidateWithContext(ctx context.Context) error {
err := validation.ValidateStructWithContext(ctx, ss,
validation.Field(&ss.Schema, validation.Required),
validation.Field(&ss.List, validation.Required),
)
Expand Down Expand Up @@ -115,11 +117,13 @@ func (s *Scenario) hasTags(docTags []cbc.Key) bool {
return false
}

// Validate checks the scenario for errors.
func (s *Scenario) Validate() error {
err := validation.ValidateStruct(s,
// ValidateWithContext checks the scenario for errors, using the regime in the context
// to validate the list of tags.
func (s *Scenario) ValidateWithContext(ctx context.Context) error {
r := ctx.Value(KeyRegime).(*Regime)
err := validation.ValidateStructWithContext(ctx, s,
validation.Field(&s.Types),
validation.Field(&s.Tags),
validation.Field(&s.Tags, validation.Each(InKeyDefs(r.Tags))),
validation.Field(&s.Name),
validation.Field(&s.Note),
validation.Field(&s.Codes),
Expand Down
Loading