-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
1,167 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package at_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/invopop/gobl/cbc" | ||
"github.com/invopop/gobl/org" | ||
"github.com/invopop/gobl/regimes/at" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNormalizeAndValidateTaxNumber(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
input string | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Valid PESEL with hyphen", | ||
input: "12-3456789", | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "Valid PESEL with spaces", | ||
input: "12 345 6789", | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "Valid PESEL with mixed symbols", | ||
input: "12.345.6789", | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "Invalid length", | ||
input: "12-34567", // Less than 9 digits | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "Invalid length with extra digits", | ||
input: "12-34567890", // More than 9 digits | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "Invalid tax office code", | ||
input: "00-3456789", // Tax office code should be between 1 and 99 | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "Invalid taxpayer number", | ||
input: "12-0000000", // Taxpayer number should be positive | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "Empty input", | ||
input: "", | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "Nil identity", | ||
input: "12-3456789", // This should not error out because we will check for nil | ||
wantErr: false, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
identity := &org.Identity{ | ||
Key: at.IdentityKeyTaxNumber, | ||
Code: cbc.Code(tt.input), | ||
} | ||
|
||
// Normalize the tax number first | ||
at.Normalize(identity) | ||
|
||
// Validate the tax number | ||
err := at.Validate(identity) | ||
|
||
if tt.wantErr { | ||
assert.Error(t, err) | ||
} else { | ||
assert.NoError(t, err) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package at | ||
|
||
import ( | ||
"errors" | ||
"regexp" | ||
"strconv" | ||
|
||
"github.com/invopop/gobl/cbc" | ||
"github.com/invopop/gobl/i18n" | ||
"github.com/invopop/gobl/org" | ||
"github.com/invopop/validation" | ||
) | ||
|
||
const ( | ||
// IdentityKeyTaxNumber represents the Austrian tax number (Steuernummer) issued to | ||
// people that can be included on invoices inside Austria. For international | ||
// sales, the registered VAT number (Umsatzsteueridentifikationsnummer) should | ||
// be used instead. | ||
IdentityKeyTaxNumber cbc.Key = "at-tax-number" | ||
) | ||
|
||
var badCharsRegexPattern = regexp.MustCompile(`[^\d]`) | ||
|
||
var identityKeyDefinitions = []*cbc.KeyDefinition{ | ||
{ | ||
Key: IdentityKeyTaxNumber, | ||
Name: i18n.String{ | ||
i18n.EN: "Tax Number", | ||
i18n.DE: "Steuernummer", | ||
}, | ||
}, | ||
} | ||
|
||
func normalizeTaxNumber(id *org.Identity) { | ||
if id == nil || id.Key != IdentityKeyTaxNumber { | ||
return | ||
} | ||
code := id.Code.String() | ||
code = badCharsRegexPattern.ReplaceAllString(code, "") | ||
id.Code = cbc.Code(code) | ||
} | ||
|
||
func validateTaxNumber(id *org.Identity) error { | ||
if id == nil || id.Key != IdentityKeyTaxNumber { | ||
return nil | ||
} | ||
|
||
return validation.ValidateStruct(id, | ||
validation.Field(&id.Code, validation.By(validateTaxIdCode)), | ||
) | ||
} | ||
|
||
// validateAustrianTaxIdCode validates the normalized tax ID code. | ||
func validateTaxIdCode(value interface{}) error { | ||
code, ok := value.(cbc.Code) | ||
if !ok || code == "" { | ||
return nil | ||
} | ||
val := code.String() | ||
|
||
// Austrian Steuernummer format: must have 9 digits (2 for tax office + 7 for taxpayer ID) | ||
if len(val) != 9 { | ||
return errors.New("length must be 9 digits") | ||
} | ||
|
||
// Split into tax office code and taxpayer number | ||
taxOffice, _ := strconv.Atoi(val[:2]) | ||
taxpayerNumber, _ := strconv.Atoi(val[2:]) | ||
|
||
// Perform basic checks | ||
if taxOffice < 1 || taxOffice > 99 { | ||
return errors.New("invalid tax office code") | ||
} | ||
|
||
if taxpayerNumber <= 0 { | ||
return errors.New("invalid taxpayer number") | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package fr | ||
|
||
import ( | ||
"errors" | ||
"regexp" | ||
"strconv" | ||
|
||
"github.com/invopop/gobl/cbc" | ||
"github.com/invopop/gobl/i18n" | ||
"github.com/invopop/gobl/org" | ||
"github.com/invopop/validation" | ||
) | ||
|
||
const ( | ||
// IdentityKeyTaxNumber represents the French tax reference number (numéro fiscal de référence). | ||
IdentityKeyTaxNumber cbc.Key = "fr-tax-number" | ||
) | ||
|
||
var badCharsRegexPattern = regexp.MustCompile(`[^\d]`) | ||
|
||
var identityKeyDefinitions = []*cbc.KeyDefinition{ | ||
{ | ||
Key: IdentityKeyTaxNumber, | ||
Name: i18n.String{ | ||
i18n.EN: "Tax Number", | ||
i18n.FR: "Numéro fiscal de référence", | ||
}, | ||
}, | ||
} | ||
|
||
// validateTaxNumber validates the French tax reference number. | ||
func validateTaxNumber(id *org.Identity) error { | ||
if id == nil || id.Key != IdentityKeyTaxNumber { | ||
return nil | ||
} | ||
|
||
return validation.ValidateStruct(id, | ||
validation.Field(&id.Code, validation.By(validateTaxIdCode)), | ||
) | ||
} | ||
|
||
// validateTaxIdCode validates the normalized tax ID code. | ||
func validateTaxIdCode(value interface{}) error { | ||
code, ok := value.(cbc.Code) | ||
if !ok || code == "" { | ||
return nil | ||
} | ||
val := code.String() | ||
|
||
// Check length | ||
if len(val) != 13 { | ||
return errors.New("length must be 13 digits") | ||
} | ||
|
||
// Check that all characters are digits | ||
if _, err := strconv.Atoi(val); err != nil { | ||
return errors.New("must contain only digits") | ||
} | ||
|
||
// Check that the first digit is 0, 1, 2, or 3 | ||
firstDigit := val[0] | ||
if firstDigit < '0' || firstDigit > '3' { | ||
return errors.New("first digit must be 0, 1, 2, or 3") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// normalizeTaxNumber removes any non-digit characters from the tax number. | ||
func normalizeTaxNumber(id *org.Identity) { | ||
if id == nil || id.Key != IdentityKeyTaxNumber { | ||
return | ||
} | ||
code := id.Code.String() | ||
code = badCharsRegexPattern.ReplaceAllString(code, "") | ||
id.Code = cbc.Code(code) | ||
} |
Oops, something went wrong.