From 093202a23d7a614ee2eb46e1a93b0a83163dc893 Mon Sep 17 00:00:00 2001 From: Juan Moliner Date: Fri, 24 Nov 2023 09:49:58 +0100 Subject: [PATCH] Draft DE & CA regimes --- regimes/ca/README.md | 3 + regimes/ca/ca.go | 107 ++++++++++++++++++++ regimes/ca/examples/invoice-ca-ca.yaml | 37 +++++++ regimes/ca/examples/out/invoice-ca-ca.json | 97 ++++++++++++++++++ regimes/ca/invoice_validator.go | 27 +++++ regimes/de/README.md | 3 + regimes/de/de.go | 61 +++++++++++ regimes/de/examples/invoice-de-de.yaml | 46 +++++++++ regimes/de/examples/out/invoice-de-de.json | 111 +++++++++++++++++++++ regimes/de/scenarios.go | 36 +++++++ regimes/de/tax_categories.go | 109 ++++++++++++++++++++ regimes/de/tax_identity.go | 96 ++++++++++++++++++ regimes/de/tax_identity_test.go | 93 +++++++++++++++++ regimes/regimes.go | 2 + 14 files changed, 828 insertions(+) create mode 100644 regimes/ca/README.md create mode 100644 regimes/ca/ca.go create mode 100644 regimes/ca/examples/invoice-ca-ca.yaml create mode 100644 regimes/ca/examples/out/invoice-ca-ca.json create mode 100644 regimes/ca/invoice_validator.go create mode 100644 regimes/de/README.md create mode 100644 regimes/de/de.go create mode 100644 regimes/de/examples/invoice-de-de.yaml create mode 100644 regimes/de/examples/out/invoice-de-de.json create mode 100644 regimes/de/scenarios.go create mode 100644 regimes/de/tax_categories.go create mode 100644 regimes/de/tax_identity.go create mode 100644 regimes/de/tax_identity_test.go diff --git a/regimes/ca/README.md b/regimes/ca/README.md new file mode 100644 index 00000000..3fd6bf1f --- /dev/null +++ b/regimes/ca/README.md @@ -0,0 +1,3 @@ +# 🇨🇦 GOBL Canada Tax Regime + +Example Canada GOBL files can be found in the [`examples`](./examples) (YAML uncalculated documents) and [`examples/out`](./examples/out) (JSON calculated envelopes) subdirectories. diff --git a/regimes/ca/ca.go b/regimes/ca/ca.go new file mode 100644 index 00000000..398d612d --- /dev/null +++ b/regimes/ca/ca.go @@ -0,0 +1,107 @@ +// Package ca provides models for dealing with Canada. +package ca + +import ( + "github.com/invopop/gobl/bill" + "github.com/invopop/gobl/cal" + "github.com/invopop/gobl/cbc" + "github.com/invopop/gobl/currency" + "github.com/invopop/gobl/i18n" + "github.com/invopop/gobl/l10n" + "github.com/invopop/gobl/num" + "github.com/invopop/gobl/regimes/common" + "github.com/invopop/gobl/tax" +) + +func init() { + tax.RegisterRegime(New()) +} + +// Identification codes unique to the United States. +const ( + IdentityTypeEIN cbc.Code = "EIN" // Employer Identification Number +) + +// New provides the tax region definition +func New() *tax.Regime { + return &tax.Regime{ + Country: l10n.CA, + Currency: currency.CAD, + Name: i18n.String{ + i18n.EN: "Canada", + }, + TimeZone: "America/Toronto", // Toronto + Validator: Validate, + Categories: []*tax.Category{ + // + // General Sales Tax (GST) + // + { + Code: common.TaxCategoryGST, + Name: i18n.String{ + i18n.EN: "GST", + }, + Title: i18n.String{ + i18n.EN: "General Sales Tax", + }, + Sources: []*tax.Source{ + { + Title: i18n.String{ + i18n.EN: "GST/HST provincial rates table", + }, + URL: "https://www.canada.ca/en/revenue-agency/services/tax/businesses/topics/gst-hst-businesses/charge-collect-which-rate/calculator.html", + }, + }, + Retained: false, + Rates: []*tax.Rate{ + { + Key: common.TaxRateZero, + Name: i18n.String{ + i18n.EN: "Zero Rate", + }, + Description: i18n.String{ + i18n.EN: "Some supplies are zero-rated under the GST, mainly: basic groceries, agricultural products, farm livestock, most fishery products such, prescription drugs and drug-dispensing services, certain medical devices, feminine hygiene products, exports, many transportation services where the origin or destination is outside Canada", + }, + Values: []*tax.RateValue{ + { + Percent: num.MakePercentage(0, 3), + }, + }, + }, + { + Key: common.TaxRateStandard, + Name: i18n.String{ + i18n.EN: "Standard rate", + }, + Description: i18n.String{ + i18n.EN: "For the majority of sales of goods and services: it applies to all products or services for which no other rate is expressly provided.", + }, + + Values: []*tax.RateValue{ + { + Since: cal.NewDate(2022, 1, 1), + Percent: num.MakePercentage(5, 2), + }, + }, + }, + }, + }, + // + // [TODO] Harmonized Sales Tax (HST) + // + + // + // [TODO] Provincial Sales Tax (PST) + // + }, + } +} + +// Validate checks the document type and determines if it can be validated. +func Validate(doc interface{}) error { + switch obj := doc.(type) { + case *bill.Invoice: + return validateInvoice(obj) + } + return nil +} diff --git a/regimes/ca/examples/invoice-ca-ca.yaml b/regimes/ca/examples/invoice-ca-ca.yaml new file mode 100644 index 00000000..b062700f --- /dev/null +++ b/regimes/ca/examples/invoice-ca-ca.yaml @@ -0,0 +1,37 @@ +$schema: "https://gobl.org/draft-0/bill/invoice" +currency: "CAD" +issue_date: "2023-04-21" +series: "SAMPLE" +code: "001" + +supplier: + tax_id: + country: "CA" + name: "Provide One Inc." + emails: + - addr: "billing@provideone.com" + addresses: + - num: "151" + street: "O'Connor Street" + locality: "Ottawa" + region: "ON" + code: "K2P 2L8" + country: "CA" + +customer: + name: "Sample Consumer" + emails: + - addr: "email@sample.com" + +lines: + - quantity: 20 + item: + name: "Development services" + price: "90.00" + unit: "h" + discounts: + - percent: "10%" + reason: "Special discount" + taxes: + - cat: GST + percent: "8.5%" diff --git a/regimes/ca/examples/out/invoice-ca-ca.json b/regimes/ca/examples/out/invoice-ca-ca.json new file mode 100644 index 00000000..470ab043 --- /dev/null +++ b/regimes/ca/examples/out/invoice-ca-ca.json @@ -0,0 +1,97 @@ +{ + "$schema": "https://gobl.org/draft-0/envelope", + "head": { + "uuid": "8a51fd30-2a27-11ee-be56-0242ac120002", + "dig": { + "alg": "sha256", + "val": "3d1cd0dd9fb113e244bddbdb7cefdb8be5c1a9ab3d60928cc225303cc1a2326b" + }, + "draft": true + }, + "doc": { + "$schema": "https://gobl.org/draft-0/bill/invoice", + "type": "standard", + "series": "SAMPLE", + "code": "001", + "issue_date": "2023-04-21", + "currency": "CAD", + "supplier": { + "name": "Provide One Inc.", + "tax_id": { + "country": "CA" + }, + "addresses": [ + { + "num": "151", + "street": "O'Connor Street", + "locality": "Ottawa", + "region": "ON", + "code": "K2P 2L8", + "country": "CA" + } + ], + "emails": [ + { + "addr": "billing@provideone.com" + } + ] + }, + "customer": { + "name": "Sample Consumer", + "emails": [ + { + "addr": "email@sample.com" + } + ] + }, + "lines": [ + { + "i": 1, + "quantity": "20", + "item": { + "name": "Development services", + "price": "90.00", + "unit": "h" + }, + "sum": "1800.00", + "discounts": [ + { + "percent": "10%", + "amount": "180.00", + "reason": "Special discount" + } + ], + "taxes": [ + { + "cat": "GST", + "percent": "8.5%" + } + ], + "total": "1620.00" + } + ], + "totals": { + "sum": "1620.00", + "total": "1620.00", + "taxes": { + "categories": [ + { + "code": "GST", + "rates": [ + { + "base": "1620.00", + "percent": "8.5%", + "amount": "137.70" + } + ], + "amount": "137.70" + } + ], + "sum": "137.70" + }, + "tax": "137.70", + "total_with_tax": "1757.70", + "payable": "1757.70" + } + } +} \ No newline at end of file diff --git a/regimes/ca/invoice_validator.go b/regimes/ca/invoice_validator.go new file mode 100644 index 00000000..35e789bf --- /dev/null +++ b/regimes/ca/invoice_validator.go @@ -0,0 +1,27 @@ +package ca + +import ( + "github.com/invopop/gobl/bill" + "github.com/invopop/gobl/currency" + "github.com/invopop/validation" +) + +// invoiceValidator adds validation checks to invoices which are relevant +// for the region. +type invoiceValidator struct { + inv *bill.Invoice +} + +func validateInvoice(inv *bill.Invoice) error { + v := &invoiceValidator{inv: inv} + return v.validate() +} + +func (v *invoiceValidator) validate() error { + inv := v.inv + return validation.ValidateStruct(inv, + validation.Field(&inv.Currency, validation.In(currency.CAD)), + validation.Field(&inv.Supplier, validation.Required), + validation.Field(&inv.Customer), + ) +} diff --git a/regimes/de/README.md b/regimes/de/README.md new file mode 100644 index 00000000..ceb844b6 --- /dev/null +++ b/regimes/de/README.md @@ -0,0 +1,3 @@ +# 🇩🇪 GOBL Germany Tax Regime + +Example DE GOBL files can be found in the [`examples`](./examples) (YAML uncalculated documents) and [`examples/out`](./examples/out) (JSON calculated envelopes) subdirectories. diff --git a/regimes/de/de.go b/regimes/de/de.go new file mode 100644 index 00000000..f3d4d9dd --- /dev/null +++ b/regimes/de/de.go @@ -0,0 +1,61 @@ +// Package de provides the tax region definition for Germany. +package de + +import ( + "github.com/invopop/gobl/bill" + "github.com/invopop/gobl/cbc" + "github.com/invopop/gobl/i18n" + "github.com/invopop/gobl/l10n" + "github.com/invopop/gobl/tax" +) + +func init() { + tax.RegisterRegime(New()) +} + +// New provides the tax region definition +func New() *tax.Regime { + return &tax.Regime{ + Country: l10n.DE, + Currency: "EUR", + Name: i18n.String{ + i18n.EN: "Germany", + i18n.FR: "Deutschland", + }, + TimeZone: "Europe/Berlin", + Tags: invoiceTags, + Scenarios: []*tax.ScenarioSet{ + invoiceScenarios, + }, + Corrections: []*tax.CorrectionDefinition{ + { + Schema: bill.ShortSchemaInvoice, + // Germany only supports credit notes to correct an invoice + Types: []cbc.Key{ + bill.InvoiceTypeCreditNote, + }, + }, + }, + Validator: Validate, + Calculator: Calculate, + Categories: taxCategories, + } +} + +// Validate checks the document type and determines if it can be validated. +func Validate(doc interface{}) error { + switch obj := doc.(type) { + case *tax.Identity: + return validateTaxIdentity(obj) + } + return nil +} + +// Calculate will attempt to clean the object passed to it. +func Calculate(doc interface{}) error { + switch obj := doc.(type) { + case *tax.Identity: + return normalizeTaxIdentity(obj) + } + return nil +} diff --git a/regimes/de/examples/invoice-de-de.yaml b/regimes/de/examples/invoice-de-de.yaml new file mode 100644 index 00000000..629eba14 --- /dev/null +++ b/regimes/de/examples/invoice-de-de.yaml @@ -0,0 +1,46 @@ +$schema: "https://gobl.org/draft-0/bill/invoice" +currency: "EUR" +issue_date: "2022-02-01" +series: "SAMPLE" +code: "001" + +supplier: + tax_id: + country: "DE" + code: "44732829320" # random + name: "Provide One GmbH" + emails: + - addr: "billing@example.com" + addresses: + - num: "16" + street: "Dietmar-Hopp-Allee" + locality: "Walldorf" + code: "69190" + country: "DE" + +customer: + tax_id: + country: "DE" + code: "356000000" + name: "Sample Consumer" + emails: + - addr: "email@sample.com" + addresses: + - num: "25" + street: "Werner-Heisenberg-Allee" + locality: "München" + code: "80939" + country": "DE" + +lines: + - quantity: 20 + item: + name: "Development services" + price: "90.00" + unit: "h" + discounts: + - percent: "10%" + reason: "Special discount" + taxes: + - cat: VAT + rate: standard diff --git a/regimes/de/examples/out/invoice-de-de.json b/regimes/de/examples/out/invoice-de-de.json new file mode 100644 index 00000000..52b5b43e --- /dev/null +++ b/regimes/de/examples/out/invoice-de-de.json @@ -0,0 +1,111 @@ +{ + "$schema": "https://gobl.org/draft-0/envelope", + "head": { + "uuid": "8a51fd30-2a27-11ee-be56-0242ac120002", + "dig": { + "alg": "sha256", + "val": "2f42047d1a3af0dd5a1ba9c62a74340b128f8d7bf3d628fe3c16e89e972199fe" + }, + "draft": true + }, + "doc": { + "$schema": "https://gobl.org/draft-0/bill/invoice", + "type": "standard", + "series": "SAMPLE", + "code": "001", + "issue_date": "2022-02-01", + "currency": "EUR", + "supplier": { + "name": "Provide One GmbH", + "tax_id": { + "country": "DE", + "code": "44732829320" + }, + "addresses": [ + { + "num": "16", + "street": "Dietmar-Hopp-Allee", + "locality": "Walldorf", + "code": "69190", + "country": "DE" + } + ], + "emails": [ + { + "addr": "billing@example.com" + } + ] + }, + "customer": { + "name": "Sample Consumer", + "tax_id": { + "country": "DE", + "code": "39356000000" + }, + "addresses": [ + { + "num": "25", + "street": "Werner-Heisenberg-Allee", + "locality": "München", + "code": "80939" + } + ], + "emails": [ + { + "addr": "email@sample.com" + } + ] + }, + "lines": [ + { + "i": 1, + "quantity": "20", + "item": { + "name": "Development services", + "price": "90.00", + "unit": "h" + }, + "sum": "1800.00", + "discounts": [ + { + "percent": "10%", + "amount": "180.00", + "reason": "Special discount" + } + ], + "taxes": [ + { + "cat": "VAT", + "rate": "standard", + "percent": "19%" + } + ], + "total": "1620.00" + } + ], + "totals": { + "sum": "1620.00", + "total": "1620.00", + "taxes": { + "categories": [ + { + "code": "VAT", + "rates": [ + { + "key": "standard", + "base": "1620.00", + "percent": "19%", + "amount": "307.80" + } + ], + "amount": "307.80" + } + ], + "sum": "307.80" + }, + "tax": "307.80", + "total_with_tax": "1927.80", + "payable": "1927.80" + } + } +} \ No newline at end of file diff --git a/regimes/de/scenarios.go b/regimes/de/scenarios.go new file mode 100644 index 00000000..b3518a04 --- /dev/null +++ b/regimes/de/scenarios.go @@ -0,0 +1,36 @@ +package de + +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.DE: "Umkehr der Steuerschuld", + }, + }, +} + +var invoiceScenarios = &tax.ScenarioSet{ + Schema: bill.ShortSchemaInvoice, + List: []*tax.Scenario{ + // ** Special Messages ** + // Reverse Charges + { + Tags: []cbc.Key{common.TagReverseCharge}, + Note: &cbc.Note{ + Key: cbc.NoteKeyLegal, + Src: common.TagReverseCharge, + Text: "Reverse Charge / Umkehr der Steuerschuld.", + }, + }, + }, +} diff --git a/regimes/de/tax_categories.go b/regimes/de/tax_categories.go new file mode 100644 index 00000000..167949c8 --- /dev/null +++ b/regimes/de/tax_categories.go @@ -0,0 +1,109 @@ +package de + +import ( + "github.com/invopop/gobl/cal" + "github.com/invopop/gobl/i18n" + "github.com/invopop/gobl/num" + "github.com/invopop/gobl/regimes/common" + "github.com/invopop/gobl/tax" +) + +var taxCategories = []*tax.Category{ + // + // VAT + // + { + Code: common.TaxCategoryVAT, + Name: i18n.String{ + i18n.EN: "VAT", + i18n.DE: "MwSt", + }, + Title: i18n.String{ + i18n.EN: "Value Added Tax", + i18n.DE: "Mehrwertsteuer", + }, + Sources: []*tax.Source{ + { + Title: i18n.String{ + i18n.EN: "Value Added Tax/Goods and Services Tax (VAT/GST) (1976-2023)", + i18n.DE: "Umsatzsteuer/Güter - und Dienstleistungssteuer (USt/GST) (1976-2023)", + }, + URL: "https://www.oecd.org/tax/tax-policy/tax-database/", + }, + }, + Retained: false, + Rates: []*tax.Rate{ + { + Key: common.TaxRateZero, + Name: i18n.String{ + i18n.EN: "Zero Rate", + i18n.DE: "Nullsatz", + }, + Values: []*tax.RateValue{ + { + Percent: num.MakePercentage(0, 3), + }, + }, + }, + { + Key: common.TaxRateStandard, + Name: i18n.String{ + i18n.EN: "Standard rate", + i18n.DE: "Standardsteuersatz", + }, + Description: i18n.String{ + i18n.EN: "For the majority of sales of goods and services: it applies to all products or services for which no other rate is expressly provided.", + i18n.DE: "Für den Großteil der Verkäufe von Waren und Dienstleistungen gilt: Dies gilt für alle Produkte oder Dienstleistungen, für die ausdrücklich kein anderer Satz festgelegt ist.", + }, + + Values: []*tax.RateValue{ + { + Since: cal.NewDate(2022, 1, 1), + Percent: num.MakePercentage(19, 2), + }, + { + Since: cal.NewDate(2020, 7, 1), // COVID temporary measures + Percent: num.MakePercentage(16, 2), + }, + { + Since: cal.NewDate(2007, 7, 1), + Percent: num.MakePercentage(19, 2), + }, + { + Since: cal.NewDate(1993, 1, 1), + Percent: num.MakePercentage(16, 2), + }, + }, + }, + { + Key: common.TaxRateReduced, + Name: i18n.String{ + i18n.EN: "Reduced rate", + i18n.DE: "Verminderter Steuersatz", + }, + Description: i18n.String{ + i18n.EN: "Applicable in particular to basic foodstuffs, books and magazines, cultural events, hotel accommodations, public transportation, medical products, or home renovation.", + i18n.DE: "Insbesondere anwendbar auf Grundnahrungsmittel, Bücher und Zeitschriften, kulturelle Veranstaltungen, Hotelunterkünfte, öffentliche Verkehrsmittel, medizinische Produkte oder Hausrenovierung.", + }, + Values: []*tax.RateValue{ + { + Since: cal.NewDate(2022, 1, 1), + Percent: num.MakePercentage(7, 2), + }, + { + Since: cal.NewDate(2020, 7, 1), // COVID temporary measures + Percent: num.MakePercentage(5, 2), + }, + { + Since: cal.NewDate(2007, 7, 1), + Percent: num.MakePercentage(7, 2), + }, + { + Since: cal.NewDate(1993, 1, 1), + Percent: num.MakePercentage(5, 2), + }, + }, + }, + }, + }, +} diff --git a/regimes/de/tax_identity.go b/regimes/de/tax_identity.go new file mode 100644 index 00000000..829515e1 --- /dev/null +++ b/regimes/de/tax_identity.go @@ -0,0 +1,96 @@ +package de + +import ( + "errors" + "fmt" + "regexp" + "strconv" + + "github.com/invopop/gobl/cbc" + "github.com/invopop/gobl/regimes/common" + "github.com/invopop/gobl/tax" + "github.com/invopop/validation" +) + +var ( + taxCodeVATRegexp = regexp.MustCompile(`^\d{11}$`) + taxCodeSIRENRegexp = regexp.MustCompile(`^\d{9}$`) +) + +// normalizeTaxIdentity normalizes the SIREN code and ensures it includes the VAT check digits. +func normalizeTaxIdentity(tID *tax.Identity) error { + if tID.Code == "" { + return nil + } + if err := common.NormalizeTaxIdentity(tID); err != nil { + return err + } + str := tID.Code.String() + if len(str) == 9 { + // Check is we have a SIREN + if err := validateSIRENTaxCode(tID.Code); err != nil { + return err + } + chk := calculateVATCheckDigit(str) + tID.Code = cbc.Code(fmt.Sprintf("%s%s", chk, str)) + } + return nil +} + +// validateTaxIdentity checks to ensure the SIRET code looks okay. +func validateTaxIdentity(tID *tax.Identity) error { + return validation.ValidateStruct(tID, + validation.Field(&tID.Code, validation.Required, validation.By(validateVATTaxCode)), + ) +} + +func validateVATTaxCode(value interface{}) error { + code, ok := value.(cbc.Code) + if !ok || code == "" { + return nil + } + str := code.String() + + if !taxCodeVATRegexp.MatchString(str) { + return errors.New("invalid format") + } + + // Extract the last nine digits as an integer. + siren := str[2:] // extract last nine digits + chk := calculateVATCheckDigit(siren) + expectStr := str[:2] // compare with first two digits + if chk != expectStr { + return errors.New("checksum mismatch") + } + + return nil +} + +func calculateVATCheckDigit(str string) string { + // Assume we have a SIREN + total, _ := strconv.Atoi(str) + total = (total*100 + 12) % 97 + + return fmt.Sprintf("%02d", total) +} + +func validateSIRENTaxCode(value interface{}) error { + code, ok := value.(cbc.Code) + if !ok || code == "" { + return nil + } + str := code.String() + + if !taxCodeSIRENRegexp.MatchString(str) { + return errors.New("invalid format") + } + + base := str[:8] + chk := str[8:] + v := common.ComputeLuhnCheckDigit(base) + if chk != v { + return errors.New("checksum mismatch") + } + + return nil +} diff --git a/regimes/de/tax_identity_test.go b/regimes/de/tax_identity_test.go new file mode 100644 index 00000000..7492a858 --- /dev/null +++ b/regimes/de/tax_identity_test.go @@ -0,0 +1,93 @@ +package de_test + +import ( + "testing" + + "github.com/invopop/gobl/cbc" + "github.com/invopop/gobl/l10n" + "github.com/invopop/gobl/regimes/de" + "github.com/invopop/gobl/tax" + "github.com/stretchr/testify/assert" +) + +func TestNormalizeTaxIdentity(t *testing.T) { + r := de.New() + tests := []struct { + Code cbc.Code + Expected cbc.Code + }{ + { + Code: "44 73282 93 ", + Expected: "447328293", + }, + { + Code: "391-838-0", + Expected: "443918380", + }, + { + Code: "FR3918380", + Expected: "443918380", + }, + { + Code: "FR443918380", + Expected: "443918380", + }, + } + for _, ts := range tests { + tID := &tax.Identity{Country: l10n.DE, Code: ts.Code} + err := r.CalculateObject(tID) + assert.NoError(t, err) + assert.Equal(t, ts.Expected, tID.Code) + } +} + +func TestValidateTaxIdentity(t *testing.T) { + tests := []struct { + name string + code cbc.Code + err string + }{ + {name: "good 1", code: "39356000000"}, + {name: "good 2", code: "44732829320"}, + {name: "good 3", code: "44391838042"}, + { + name: "empty", + code: "", + err: "code: cannot be blank", + }, + { + name: "too long", + code: "44123456789100", + err: "invalid format", + }, + { + name: "too short", + code: "123456", + err: "invalid format", + }, + { + name: "not normalized", + code: "12.449.965-4", + err: "invalid format", + }, + { + name: "bad checksum", + code: "44999999991", + err: "checksum mismatch", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tID := &tax.Identity{Country: l10n.DE, Code: tt.code} + err := de.Validate(tID) + if tt.err == "" { + assert.NoError(t, err) + } else { + if assert.Error(t, err) { + assert.Contains(t, err.Error(), tt.err) + } + } + }) + } +} diff --git a/regimes/regimes.go b/regimes/regimes.go index b4ccad33..60371b45 100644 --- a/regimes/regimes.go +++ b/regimes/regimes.go @@ -5,7 +5,9 @@ package regimes import ( // Import all the regime definitions which will automatically // add themselves to the tax regime register. + _ "github.com/invopop/gobl/regimes/ca" _ "github.com/invopop/gobl/regimes/co" + _ "github.com/invopop/gobl/regimes/de" _ "github.com/invopop/gobl/regimes/es" _ "github.com/invopop/gobl/regimes/fr" _ "github.com/invopop/gobl/regimes/gb"