// Tax, gross and net.
//
// Two questions cover almost every invoice: what tax does this net amount
// attract, and what was the net behind this gross amount. Both are answered
// with an exact rate and a single rounding step.
///|
/// The tax an amount attracts at a rate.
pub fn Money::tax_on(
self : Money,
rate : Percent,
mode~ : RoundingMode = HalfUp,
) -> Money raise MoneyError {
self.percent(rate, mode~)
}
///|
/// The net amount plus its tax.
pub fn Money::with_tax(
self : Money,
rate : Percent,
mode~ : RoundingMode = HalfUp,
) -> Money raise MoneyError {
self.add_percent(rate, mode~)
}
///|
/// One rate's worth of an invoice: the net it applied to, the tax it produced
/// and the gross that results.
pub(all) struct TaxLine {
rate : Percent
net : Money
tax : Money
gross : Money
} derive(Debug)
///|
pub extend TaxLine with @debug.Debug::{to_repr}
///|
/// Recovers the net and the tax from a tax-inclusive amount.
///
/// The tax is the difference between the gross and the recovered net, so the
/// two parts always add back up to the gross. A rate of -100% or less cannot be
/// reversed and is refused.
pub fn Money::net_from_gross(
gross : Money,
rate : Percent,
mode~ : RoundingMode = HalfUp,
) -> (Money, Money) raise MoneyError {
let denominator = add_checked(10000L, rate.bp(), "tax rate")
if denominator <= 0L {
raise InvalidAmount(
rate.text(),
"a rate of -100% or less cannot be reversed",
)
}
let net_minor = mul_div_round(gross.minor, 10000L, denominator, mode)
let net = { minor: net_minor, currency: gross.currency, }
(net, gross.sub(net))
}
///|
/// One line per rate, each rate applied to the same net amount.
///
/// This models the taxes that stack on a single net -- a sales tax plus a local
/// levy, say -- rather than a basket of differently taxed items. The gross of
/// each line is the net plus that line's tax, so the lines can be printed
/// directly.
pub fn Money::tax_lines(
self : Money,
rates : Array[Percent],
mode~ : RoundingMode = HalfUp,
) -> Array[TaxLine] raise MoneyError {
let lines : Array[TaxLine] = []
for rate in rates {
let tax = self.tax_on(rate, mode~)
lines.push({ rate, net: self, tax, gross: self.add(tax), })
}
lines
}