///|
fn balance_for_order(
input : LedgerInput,
order : OrderRecord,
policy : Policy,
) -> OrderBalance {
let captured = sum_captured(input, order.id)
let refunded = sum_refunded(input, order.id)
let adjustments = sum_adjustments(input, order.id)
let expected = order.amount_minor + adjustments
let outstanding = expected - captured + refunded
let resolution = if refunded > captured {
RefundRisk
} else if outstanding > policy.amount_tolerance_minor {
AwaitingPayment
} else if outstanding < -policy.amount_tolerance_minor {
OverCollected
} else {
Settled
}
{
order_id: order.id,
expected_minor: expected,
captured_minor: captured,
refunded_minor: refunded,
adjustments_minor: adjustments,
outstanding_minor: outstanding,
resolution,
}
}
///|
fn balance_findings(
input : LedgerInput,
balance : OrderBalance,
current_day : Int,
policy : Policy,
findings : Array[Finding],
) -> Unit {
match find_order(input, balance.order_id) {
None => ()
Some(order) => {
if balance.refunded_minor > balance.captured_minor {
findings.push(
make_finding(
RefundExceedsPayment,
Error,
Order,
order.id,
order.id,
"refund total exceeds captured payment total",
),
)
}
if balance.outstanding_minor < -policy.amount_tolerance_minor {
findings.push(
make_finding(
Overpaid,
Error,
Order,
order.id,
order.id,
"captured amount exceeds expected amount",
),
)
}
if balance.outstanding_minor > policy.amount_tolerance_minor {
findings.push(
make_finding(
Underpaid,
Warning,
Order,
order.id,
order.id,
"order remains underpaid",
),
)
if current_day - order.created_day > policy.max_open_age_days {
findings.push(
make_finding(
OpenPastDue,
Warning,
Order,
order.id,
order.id,
"open order exceeded configured age",
),
)
}
}
}
}
}
///|
pub fn reconcile(
input : LedgerInput,
policy : Policy,
current_day : Int,
) -> ReconciliationResult {
let findings = validate(input, policy)
let balances : Array[OrderBalance] = []
for order in input.orders {
let balance = balance_for_order(input, order, policy)
balances.push(balance)
balance_findings(input, balance, current_day, policy, findings)
}
let mut valid = true
for finding in findings {
if finding.severity == Error {
valid = false
}
}
{ balances, findings, valid, }
}