/** * Task 1771 — receivables and payables ageing. * * Buckets are inclusive-lower on whole days past the due date: 0-30 is * `current`, 31-60, 61-90, and 91 and above is `d90plus`. An invoice not yet * due has a negative age, which clamps to 0 and lands in `current`. * * Every bucket is a summed money amount, not a count of invoices. Summing is * done in minor units so a statement of many decimal amounts does not drift. */ import { toMinor, fromMinor } from './money.js' const DAY_MS = 86_400_000 export interface OpenInvoice { ref: string dueDateMs: number outstanding: number } export interface AgeBuckets { current: number d31to60: number d61to90: number d90plus: number total: number } export function ageBuckets(invoices: readonly OpenInvoice[], now: number): AgeBuckets { let current = 0 let d31to60 = 0 let d61to90 = 0 let d90plus = 0 for (const inv of invoices) { const ageDays = Math.max(0, Math.floor((now - inv.dueDateMs) / DAY_MS)) const minor = toMinor(inv.outstanding) if (ageDays <= 30) current += minor else if (ageDays <= 60) d31to60 += minor else if (ageDays <= 90) d61to90 += minor else d90plus += minor } return { current: fromMinor(current), d31to60: fromMinor(d31to60), d61to90: fromMinor(d61to90), d90plus: fromMinor(d90plus), total: fromMinor(current + d31to60 + d61to90 + d90plus), } }