///|
pub fn settlement_line(
code : String,
amount : Money,
taxable : Bool,
) -> SettlementLine {
{ code, amount, taxable }
}
///|
pub fn invoice(
id : String,
lines : Array[SettlementLine],
tax : Money,
paid : Money,
) -> Invoice {
{ id, lines, tax, paid }
}
///|
pub fn invoice_subtotal(invoice : Invoice) -> Money {
let mut total = Money::zero()
for line in invoice.lines {
total = total.add(line.amount)
}
total
}
///|
pub fn invoice_total(invoice : Invoice) -> Money {
invoice_subtotal(invoice).add(invoice.tax)
}
///|
pub fn invoice_outstanding(invoice : Invoice) -> Money {
invoice_total(invoice).sub(invoice.paid).max(Money::zero())
}
///|
pub fn invoice_balance(invoice : Invoice) -> (BalanceState, Money) {
let total = invoice_total(invoice)
if invoice.paid.cents <= 0 {
(Unpaid, total)
} else if invoice.paid.cents < total.cents {
(PartiallyPaid, invoice_outstanding(invoice))
} else if invoice.paid.cents == total.cents {
(Paid, Money::zero())
} else {
(Overpaid, invoice.paid.sub(total))
}
}
///|
pub fn Invoice::taxable_subtotal(self : Invoice) -> Money {
let mut total = Money::zero()
for line in self.lines {
if line.taxable {
total = total.add(line.amount)
}
}
total
}
///|
pub fn Invoice::line_count(self : Invoice) -> Int {
self.lines.length()
}
///|
pub fn Invoice::is_closed(self : Invoice) -> Bool {
match invoice_balance(self).0 {
Paid => true
Overpaid => true
_ => false
}
}
///|
pub fn allocate_payment(
invoice : Invoice,
payment_id : String,
amount : Money,
) -> PaymentAllocation {
let remaining = invoice_outstanding(invoice)
.sub(amount.max(Money::zero()))
.max(Money::zero())
{
payment_id,
invoice_id: invoice.id,
amount: amount.max(Money::zero()).min(invoice_outstanding(invoice)),
remaining,
}
}
///|
pub fn PaymentAllocation::fully_settles(self : PaymentAllocation) -> Bool {
self.remaining.is_zero()
}
///|
pub fn BalanceState::is_terminal(self : BalanceState) -> Bool {
match self {
Paid => true
Overpaid => true
_ => false
}
}