accounting

package
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2020 License: Apache-2.0 Imports: 7 Imported by: 0

README

Accounting

The accounting package is used when dealing with currency math that requires a high degree of precision.

A few business rules and assumptions are made:

Examples:

package main

import (
        "fmt"

        "github.com/LUSHDigital/core-lush/accounting"
        "github.com/LUSHDigital/core-lush/accounting/currency"
)

func main() {
        // Create an amount from an int64 and a currency
        amount := accounting.MakeAmount(currency.GBP, 1234)
        
        // Amounts contain the minor representation of that currency. 
        fmt.Println(amount.MinorValue)
        // output: 1234
 
        // alternatively floats are also ok!
        gbp := accounting.Float64ToAmount(currency.GBP, 32.32)
        fmt.Printf("minor value: %d, stringer: %s", gbp.MinorValue, gbp)
        // output: minor value: 3232, stringer: 32.32 GBP

        // Currencies with no minor decimals drop the invalid precision.
        jpy := accounting.Float64ToAmount(currency.JPY, 32.32)
        fmt.Printf("minor value: %d, stringer: %s", jpy.MinorValue, jpy)
        // output: minor value: 32, stringer: 32 JPY

        // Exchanging currencies is also supported.
        usd := accounting.Float64ToAmount(currency.USD, 100.0)
        eur, err := accounting.Exchange(usd, currency.EUR, 1.08968)
        if err != nil {
                // handle...
        }
        fmt.Println(eur)
        // output: 91.77 EUR
}

Documentation

Index

Examples

Constants

View Source
const (
	// QuadruplePrecision describes 128 bits of precision for IEEE 754 decimals
	// see: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
	QuadruplePrecision uint = 128

	// OctuplePrecision describes 256 bits of precision for for IEEE 754 decimals
	// see: https://en.wikipedia.org/wiki/Octuple-precision_floating-point_format
	OctuplePrecision uint = 256
)

Variables

View Source
var (
	// ErrSubZeroRate happens when a rate is lower than zero.
	ErrSubZeroRate = errors.New("rate must not be less than zero")
	// ErrSubZeroGross happens when the gross amount is less than zero.
	ErrSubZeroGross = errors.New("gross amount must not be less than zero")
	// ErrSubZeroNet happens when the net amount is less than zero.
	ErrSubZeroNet = errors.New("net amount must not be less than zero")
	// ErrNetOverGrossAmount happens when the net amount is higher than the gross amount.
	ErrNetOverGrossAmount = errors.New("net amount must be equal to or lower than the gross amount")
)

Functions

func AmountToFloat64

func AmountToFloat64(amount Amount) float64

AmountToFloat64 returns the currency data as a floating point from it's minor currency unit format.

func NetAmount

func NetAmount(gross int64, rate float64) (int64, error)

NetAmount derives the net amount before tax is applied using the given rate.

func RatNetAmount

func RatNetAmount(gross, rate *big.Rat) (*big.Float, error)

RatNetAmount applies a VAT rate to a big.Rat value. This method returns a big.Float so it's accuracy can be checked, and it's value applied with .Rat(some.field)

func TaxAmount

func TaxAmount(gross, net int64) (int64, error)

TaxAmount returns the difference between the gross and the net amounts.

func ValidateFloatIsPrecise

func ValidateFloatIsPrecise(f float64) error

ValidateFloatIsPrecise ensures that a float64 value does not exceed a precision of 2 digits past the decimal period. This ensures we do not store incorrect currency data. NOTE: 2 digits past the decimal period is a business rule.

func ValidateManyFloatsArePrecise

func ValidateManyFloatsArePrecise(args ...float64) error

ValidateManyFloatsArePrecise tests that the given float64 arguments have the desired precision, this is a convenience wrapper around ValidateFloatIsPrecise. NOTE: 2 digits past the dot is a business rule.

Types

type Amount

type Amount struct {
	Currency   currency.Currency
	MinorValue int64
}

Amount defines an amount in a given currency, in it's minor unit form.

func Exchange

func Exchange(amount Amount, c currency.Currency, rate float64) (Amount, error)

Exchange - Apply currency exchange rates to an amount.

rate - should always be given from the approved finance list. NOTE: A rate of zero will return the amount you put in, unchanged.

Rounding to the nearest even is a defined business rule. Tills may round up to the nearest penny, but for reporting, the rule is always to use banker's rounding.

If unclear, see: // http://wiki.c2.com/?BankersRounding.

Example (Usd_eur)
package main

import (
	"fmt"

	"github.com/LUSHDigital/core-lush/accounting"
	"github.com/LUSHDigital/core-lush/accounting/currency"
)

func main() {
	// 4 May 2020 08:00 UTC - 5 May 2020 08:01 UTC
	// EUR/USD close:1.08968 low:1.08871 high:1.09479
	usd := accounting.Float64ToAmount(currency.USD, 100.0)
	eur, err := accounting.Exchange(usd, currency.EUR, 1.08968)
	if err != nil {
		// handle...
	}
	fmt.Println(eur)
}
Output:

91.77 EUR
Example (Usd_jpy)
package main

import (
	"fmt"

	"github.com/LUSHDigital/core-lush/accounting"
	"github.com/LUSHDigital/core-lush/accounting/currency"
)

func main() {
	// 4 May 2020 07:40 UTC - 5 May 2020 07:40 UTC
	// JPY/USD close:0.00937 low:0.00934 high:0.00939
	usd := accounting.Float64ToAmount(currency.USD, 100.0)
	jpy, err := accounting.Exchange(usd, currency.JPY, 0.00937)
	if err != nil {
		// handle...
	}
	fmt.Println(jpy)
}
Output:

10672 JPY

func Float64ToAmount

func Float64ToAmount(c currency.Currency, value float64) Amount

Float64ToAmount returns an amount from the provided currency and value.

Example (Gbp)
package main

import (
	"fmt"

	"github.com/LUSHDigital/core-lush/accounting"
	"github.com/LUSHDigital/core-lush/accounting/currency"
)

func main() {
	gbp := accounting.Float64ToAmount(currency.GBP, 32.32)
	fmt.Printf("minor value: %d, stringer: %s", gbp.MinorValue, gbp)
}
Output:

minor value: 3232, stringer: 32.32 GBP
Example (Jpy)
package main

import (
	"fmt"

	"github.com/LUSHDigital/core-lush/accounting"
	"github.com/LUSHDigital/core-lush/accounting/currency"
)

func main() {
	// JPY doesn't allow for values after the decimal dot, since it has a
	// minor currency unit of 1.
	jpy := accounting.Float64ToAmount(currency.JPY, 32.32)
	fmt.Printf("minor value: %d, stringer: %s", jpy.MinorValue, jpy)
}
Output:

minor value: 32, stringer: 32 JPY

func MakeAmount

func MakeAmount(c currency.Currency, minorValue int64) Amount

MakeAmount returns an Amount from the provided currency and minor unit value.

func (Amount) String

func (a Amount) String() string

String implements a default stringer for an Amount Note that the string will be in "human readable" format, rather than using the minor currency unit, this conversion is done using the AmountToFloat64 function, also available within this package. ISO_4217 does not regulate spacing or prefixing vs. suffixing. Strings produced using this method always follow this pattern:

   ┏━━ always decimal dot separated.
   ┃
123.45 GBP
    ┃  ┗━━ ISO currency code.
    ┗━━ maximum 2 digits precision.

type ErrFloatPrecision

type ErrFloatPrecision struct {
	Value     string
	Precision int
}

ErrFloatPrecision happens when a floating point number is not following business precision rules.

func (ErrFloatPrecision) Error

func (e ErrFloatPrecision) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL