160 lines
3.4 KiB
Go
160 lines
3.4 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})
|
|
}
|
|
|
|
func (p *Price) UnmarshalJSON(data []byte) error {
|
|
type wire struct {
|
|
ExVat int64 `json:"exVat"`
|
|
IncVat int64 `json:"incVat"`
|
|
Vat map[string]int64 `json:"vat,omitempty"`
|
|
}
|
|
var w wire
|
|
if err := json.Unmarshal(data, &w); err != nil {
|
|
return err
|
|
}
|
|
|
|
p.IncVat = w.IncVat
|
|
if len(w.Vat) > 0 {
|
|
p.VatRates = make(map[float32]int64, len(w.Vat))
|
|
for rateStr, amount := range w.Vat {
|
|
rate, err := strconv.ParseFloat(rateStr, 32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.VatRates[float32(rate)] = amount
|
|
}
|
|
} else {
|
|
p.VatRates = make(map[float32]int64)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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
|
|
}
|