Files
apskel-pos-backend/internal/processor/voucher_cost.go
T

44 lines
1.3 KiB
Go
Raw Normal View History

package processor
import (
"math/big"
"apskel-pos-be/internal/repository"
)
// VoucherCostPart is the share of a redemption's face value one source of its
// EnakPoint accounts for (docs/rfc-enakgame.md §7.6).
type VoucherCostPart struct {
repository.RedemptionPointSource
Cost int64
}
// SplitVoucherCost divides faceValue over the sources in proportion to their
// EnakPoint: floor(face_value × points / total points) each, with what rounding left
// over going to the largest part (the first of equals), so the parts add up to
// face_value exactly. Products are computed without overflow.
func SplitVoucherCost(faceValue int64, sources []repository.RedemptionPointSource) []VoucherCostPart {
parts := make([]VoucherCostPart, 0, len(sources))
var totalPoints int64
for _, s := range sources {
totalPoints += s.Points
}
if totalPoints <= 0 {
return parts
}
face, total := big.NewInt(faceValue), big.NewInt(totalPoints)
var assigned int64
largest := 0
for i, s := range sources {
share := new(big.Int).Mul(face, big.NewInt(s.Points))
share.Quo(share, total)
parts = append(parts, VoucherCostPart{RedemptionPointSource: s, Cost: share.Int64()})
assigned += share.Int64()
if s.Points > sources[largest].Points {
largest = i
}
}
parts[largest].Cost += faceValue - assigned
return parts
}