Skip to content

Commit

Permalink
fix(core): handle precision and clean rounding of Money
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin Brosse committed Jan 13, 2020
1 parent 82503e9 commit c45e992
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 10 deletions.
37 changes: 34 additions & 3 deletions scw/custom_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -77,12 +78,37 @@ type Money struct {
}

// NewMoneyFromFloat conerts a float with currency to a Money object.
func NewMoneyFromFloat(value float64, currency string) *Money {
return &Money{
//
// precision is the number of digits after the decimal point
// used to parse the nanos part of the value.
func NewMoneyFromFloat(value float64, currency string, precision int) *Money {
if precision > 9 {
panic(fmt.Errorf("max precision is 9"))
}

strValue := strconv.FormatFloat(value, 'f', precision, 64)
parts := strings.Split(strValue, ".")

money := &Money{
CurrencyCode: currency,
Units: int64(value),
Nanos: int32((value - float64(int64(value))) * 1000000000),
Nanos: 0,
}

// Handle nanos.
if len(parts) == 2 {
// Add leading zeros.
strNanos := parts[1] + "000000000"[len(parts[1]):]

n, err := strconv.ParseInt(strNanos, 10, 32)
if err != nil {
panic(fmt.Errorf("invalid nanos %s", strNanos))
}

money.Nanos = int32(n)
}

return money
}

// String returns the string representation of Money.
Expand All @@ -109,6 +135,11 @@ func (m *Money) ToFloat() float64 {
return float64(m.Units) + float64(m.Nanos)/1000000000
}

// MarshalJSON returns the JSON representation of Money.
func (m *Money) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprint(m.ToFloat())), nil
}

// Money represents a size in bytes.
type Size uint64

Expand Down
117 changes: 110 additions & 7 deletions scw/custom_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,152 @@ import (
"github.com/scaleway/scaleway-sdk-go/internal/testhelpers"
)

func TestMoney_NewMoneyFromFloat(t *testing.T) {
cases := []struct {
value float64
currency string
precision int
want *Money
}{
{
value: 0.0,
currency: "EUR",
precision: 0,
want: &Money{
CurrencyCode: "EUR",
Units: 0,
Nanos: 0,
},
},
{
value: 1.0,
currency: "EUR",
precision: 3,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 0,
},
},
{
value: 1.3,
currency: "EUR",
precision: 3,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 300000000,
},
},
{
value: 1.333,
currency: "EUR",
precision: 2,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 330000000,
},
},
{
value: 1.04,
currency: "EUR",
precision: 1,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 0,
},
},
{
value: 1.05,
currency: "EUR",
precision: 1,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 100000000,
},
},
{
value: 1.123456789,
currency: "EUR",
precision: 9,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 123456789,
},
},
{
value: 1.999999999,
currency: "EUR",
precision: 9,
want: &Money{
CurrencyCode: "EUR",
Units: 1,
Nanos: 999999999,
},
},
}

for _, c := range cases {
t.Run(c.want.String(), func(t *testing.T) {
testhelpers.Equals(t, c.want, NewMoneyFromFloat(c.value, c.currency, c.precision))
})
}
}

func TestMoney_String(t *testing.T) {
cases := []struct {
money Money
money *Money
want string
}{
{
money: Money{
money: &Money{
CurrencyCode: "EUR",
Units: 10,
},
want: "€ 10.00",
},
{
money: Money{
money: &Money{
CurrencyCode: "USD",
Units: 10,
Nanos: 1,
},
want: "$ 10.000000001",
},
{
money: Money{
money: &Money{
CurrencyCode: "EUR",
Nanos: 100000000,
},
want: "€ 0.10",
},
{
money: Money{
money: &Money{
CurrencyCode: "EUR",
Nanos: 500000,
},
want: "€ 0.0005",
},
{
money: Money{
money: &Money{
CurrencyCode: "EUR",
Nanos: 333000000,
},
want: "€ 0.333",
},
{
money: &Money{
CurrencyCode: "EUR",
Nanos: 123456789,
},
want: "€ 0.123456789",
},
{
money: Money{
money: &Money{
CurrencyCode: "?",
},
want: "? 0.00",
Expand Down

0 comments on commit c45e992

Please sign in to comment.