Co-authored-by: matst80 <mats.tornberg@gmail.com> Reviewed-on: https://git.tornberg.me/mats/go-cart-actor/pulls/6 Co-authored-by: Mats Törnberg <mats@tornberg.me> Co-committed-by: Mats Törnberg <mats@tornberg.me>
132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
package cart
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strconv"
|
|
)
|
|
|
|
func GetTaxAmount(total int64, tax int) int64 {
|
|
taxD := 10000 / float64(tax)
|
|
return int64(float64(total) / float64((1 + taxD)))
|
|
}
|
|
|
|
type Price struct {
|
|
IncVat int64 `json:"incVat"`
|
|
VatRates map[float32]int64 `json:"vat,omitempty"`
|
|
}
|
|
|
|
func NewPrice() *Price {
|
|
return &Price{
|
|
IncVat: 0,
|
|
VatRates: make(map[float32]int64),
|
|
}
|
|
}
|
|
|
|
func NewPriceFromIncVat(incVat int64, taxRate float32) *Price {
|
|
tax := GetTaxAmount(incVat, int(taxRate*100))
|
|
return &Price{
|
|
IncVat: incVat,
|
|
VatRates: map[float32]int64{
|
|
taxRate: tax,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p *Price) ValueExVat() int64 {
|
|
exVat := p.IncVat
|
|
for _, amount := range p.VatRates {
|
|
exVat -= amount
|
|
}
|
|
return exVat
|
|
}
|
|
|
|
func (p *Price) TotalVat() int64 {
|
|
total := int64(0)
|
|
for _, amount := range p.VatRates {
|
|
total += amount
|
|
}
|
|
return total
|
|
}
|
|
|
|
func MultiplyPrice(p Price, qty int64) *Price {
|
|
ret := &Price{
|
|
IncVat: p.IncVat * qty,
|
|
VatRates: make(map[float32]int64),
|
|
}
|
|
for rate, amount := range p.VatRates {
|
|
ret.VatRates[rate] = amount * qty
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (p *Price) Multiply(qty int64) {
|
|
p.IncVat *= qty
|
|
for rate, amount := range p.VatRates {
|
|
p.VatRates[rate] = amount * qty
|
|
}
|
|
}
|
|
|
|
func (p Price) MarshalJSON() ([]byte, error) {
|
|
// Build a stable wire format without calling Price.MarshalJSON recursively
|
|
exVat := p.ValueExVat()
|
|
var vat map[string]int64
|
|
if len(p.VatRates) > 0 {
|
|
vat = make(map[string]int64, len(p.VatRates))
|
|
for rate, amount := range p.VatRates {
|
|
// Rely on default formatting that trims trailing zeros for whole numbers
|
|
// Using %g could output scientific notation for large numbers; float32 rates here are small.
|
|
key := trimFloat(rate)
|
|
vat[key] = amount
|
|
}
|
|
}
|
|
type wire struct {
|
|
ExVat int64 `json:"exVat"`
|
|
IncVat int64 `json:"incVat"`
|
|
Vat map[string]int64 `json:"vat,omitempty"`
|
|
}
|
|
return json.Marshal(wire{ExVat: exVat, IncVat: p.IncVat, Vat: vat})
|
|
}
|
|
|
|
// trimFloat converts a float32 tax rate like 25 or 12.5 into a compact string without
|
|
// unnecessary decimals ("25", "12.5").
|
|
func trimFloat(f float32) string {
|
|
// Convert via FormatFloat then trim trailing zeros and dot.
|
|
s := strconv.FormatFloat(float64(f), 'f', -1, 32)
|
|
return s
|
|
}
|
|
|
|
func (p *Price) Add(price Price) {
|
|
p.IncVat += price.IncVat
|
|
for rate, amount := range price.VatRates {
|
|
p.VatRates[rate] += amount
|
|
}
|
|
}
|
|
|
|
func (p *Price) Subtract(price Price) {
|
|
p.IncVat -= price.IncVat
|
|
for rate, amount := range price.VatRates {
|
|
p.VatRates[rate] -= amount
|
|
}
|
|
}
|
|
|
|
func SumPrices(prices ...Price) *Price {
|
|
if len(prices) == 0 {
|
|
return NewPrice()
|
|
}
|
|
|
|
aggregated := NewPrice()
|
|
|
|
for _, price := range prices {
|
|
aggregated.IncVat += price.IncVat
|
|
for rate, amount := range price.VatRates {
|
|
aggregated.VatRates[rate] += amount
|
|
}
|
|
}
|
|
|
|
if len(aggregated.VatRates) == 0 {
|
|
aggregated.VatRates = nil
|
|
}
|
|
|
|
return aggregated
|
|
}
|