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

Formatting num Amounts and currency details #259

Merged
merged 6 commits into from
Mar 27, 2024
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ GOBL makes it easy to create business documents, like invoices, but checkout som
- [GOBL for Ruby](https://github.com/invopop/gobl.ruby) - Easily build or read GOBL documents in Ruby.

Conversion to local formats

- [GOBL to FacturaE (Spain)](https://github.com/invopop/gobl.facturae) - convert into the [Spanish FacturaE](https://www.facturae.gob.es/Paginas/Index.aspx) format.
- [GOBL to CFDI (Mexico)](https://github.com/invopop/gobl.cfdi) - convert into the Mexican CFDI format.
- [GOBL to FatturaPA (Italy)](https://github.com/invopop/gobl.fatturapa) - convert into the [Italian FatturaPA](https://www.fatturapa.gov.it/it/index.html) format.
Expand Down Expand Up @@ -139,3 +140,11 @@ if err := env.Insert(inv); err != nil {
panic(err)
}
```

## Development

GOBL uses the `go generate` command to automatically generate JSON schemas, definitions, and some Go code output. After any changes, be sure to run:

```bash
go generate .
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice!

```
7 changes: 3 additions & 4 deletions currency/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Currency Reference Data

Data sourced from ISO website and providers.
Currency support in GOBL aims to cover most of the currencies in the world and provide methods to format and work with them.

## ISO 4217

Currency code data sourced from: https://www.iso.org/iso-4217-currency-codes.html
Much of the inspiration for this package and source data in the `/data/currency` directory originally came from the excellent and widely used [money gem in ruby](https://rubymoney.github.io/money/). A few alterations to source data have been made.

Currencies around the world change more often than expected, please [send a PR](https://github.com/invopop/gobl/pulls) if you spot anything that is out of date, along with a link that references the source of the change.
68 changes: 17 additions & 51 deletions currency/code.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package currency

import (
"github.com/invopop/gobl/num"
"fmt"

"github.com/invopop/jsonschema"
"github.com/invopop/validation"
)

// Code is the ISO currency code
Expand All @@ -12,72 +12,38 @@ type Code string
// CodeEmpty is used when there is no code.
const CodeEmpty Code = ""

// Def provides a structure for the currencies
type Def struct {
Code Code `json:"code"` // three-letter currency code
Name string `json:"name"` // name of the currency
Num string `json:"num"` // three-digit currency code
Units uint32 `json:"units"` // how many cents are used for the currency
}

func validCodes() []interface{} {
list := make([]interface{}, len(CodeDefinitions))
for i, d := range CodeDefinitions {
list[i] = string(d.Code)
}
return list
}

var isValidCode = validation.In(validCodes()...)

// Validate ensures the currency code is valid according
// to the ISO 4217 three-letter list.
func (c Code) Validate() error {
return validation.Validate(string(c), isValidCode)
return inDefinitions(c)
}

// Def provides the currency definition for the code.
func (c Code) Def() Def {
d, _ := Get(c)
return d
}

// Get provides the code's currency definition, or
// false if none is found.
func Get(c Code) (Def, bool) {
for _, d := range CodeDefinitions {
if d.Code == c {
return d, true
}
func inDefinitions(code Code) error {
if code == CodeEmpty {
return nil
}
return Def{}, false
}

// Zero provides the currency's zero amount which is pre-set with the
// minimum precision for the currency.
func (d Def) Zero() num.Amount {
return num.MakeAmount(0, d.Units)
if d := Get(code); d == nil {
return fmt.Errorf("currency code %s not defined", code)
}
return nil
}

// BaseAmount provides a definition's zero amount with the correct decimal
// places so that it can be used as a base for calculating totals.
//
// Deprecated: please now use the Zero method instead.
func (d Def) BaseAmount() num.Amount {
return num.MakeAmount(0, d.Units)
// Def provides the currency definition for the code.
func (c Code) Def() *Def {
return Get(c)
}

// JSONSchema provides a representation of the struct for usage in Schema.
func (Code) JSONSchema() *jsonschema.Schema {
s := &jsonschema.Schema{
Title: "Currency Code",
Type: "string",
OneOf: make([]*jsonschema.Schema, len(CodeDefinitions)),
Description: "ISO Currency Code",
OneOf: make([]*jsonschema.Schema, len(Definitions())),
Description: "Currency Code as defined in the GOBL source which is expected to be ISO or commonly used alternative.",
}
for i, v := range CodeDefinitions {
for i, v := range Definitions() {
s.OneOf[i] = &jsonschema.Schema{
Const: v.Code,
Const: v.ISOCode,
Title: v.Name,
}
}
Expand Down
9 changes: 9 additions & 0 deletions currency/code_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/invopop/gobl/currency"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCode(t *testing.T) {
Expand All @@ -13,4 +14,12 @@ func TestCode(t *testing.T) {

d := c.Def()
assert.Equal(t, d.Name, "Euro")

c = currency.CodeEmpty
assert.NoError(t, c.Validate())

c = currency.Code("FOOO")
err := c.Validate()
require.Error(t, err)
assert.Contains(t, err.Error(), "currency code FOOO not defined")
}
Loading
Loading