import type { TransactionLedger } from "./transactionLedger.js"; /** * Money in this module is an integer count of **minor units** — cents when the currency has two * decimals, whole coins when it has none. Principal, interest, payments and balances are safe * integers, so a schedule adds up exactly. */ /** Terms of one loan. Everything a repayment schedule needs, as plain data. */ export interface LoanTerms { /** Stable id, unique within a loan book. */ id: string; /** Integer minor units borrowed; must be `> 0`. */ principal: number; /** Interest per period as a fraction of the balance, e.g. `0.05` for 5%. */ ratePerPeriod: number; /** Number of scheduled payments; integer `>= 1`. */ periods: number; /** Seconds between scheduled payments; must be `> 0`. */ periodSeconds: number; /** When the first payment is due, in seconds. */ startSeconds: number; /** * `"amortized"` (default) charges interest on the outstanding balance and levels the payment; * `"flat"` charges interest on the original principal and splits principal evenly. */ method?: "amortized" | "flat"; /** Currency the loan is denominated in; falls back to the book's default. */ currency?: string; /** Account that owes and pays; falls back to the book's default. */ account?: string; } /** One scheduled repayment, in integer minor units. */ export interface LoanPayment { /** 1-based. */ index: number; dueSeconds: number; interest: number; principal: number; /** `interest + principal`. */ amount: number; /** Remaining principal after this payment. */ balanceAfter: number; } /** A full repayment plan: every payment, its interest/principal split and the running balance. */ export interface LoanSchedule { id: string; principal: number; method: "amortized" | "flat"; payments: readonly LoanPayment[]; /** Sum of every payment's interest. */ totalInterest: number; /** Sum of every payment. */ totalPaid: number; } /** One live loan: its plan plus how far it has been paid. */ export interface LoanRecord { id: string; /** The terms it was drawn on, kept whole so a save stores them verbatim. */ terms: LoanTerms; principal: number; ratePerPeriod: number; periods: number; currency: string; account: string; schedule: LoanSchedule; /** Payments settled so far. */ paid: number; /** Total settled, in minor units, including interest. */ paidAmount: number; /** Interest settled so far, in minor units. */ interestPaid: number; /** Scheduled payments that came due with no funds, counted once per advance. */ missed: number; /** True once `missed` reached the book's limit; a defaulted loan stops being advanced. */ defaulted: boolean; /** True once the principal is fully repaid, early or on schedule. */ settled: boolean; /** Remaining principal, in minor units. */ outstanding: number; } /** Saved progress of one loan; the schedule is rebuilt from its terms on restore. */ export interface LoanState { terms: LoanTerms; paid: number; paidAmount: number; interestPaid: number; missed: number; defaulted: boolean; settled: boolean; outstanding: number; } /** Serializable loan-book state. */ export interface LoanBookState { loans: readonly LoanState[]; } /** What one {@link LoanBook.advance} settled, and what it could not. */ export interface LoanAdvanceResult { paid: readonly LoanPayment[]; /** Payments that came due with no funds; each counts once per advance. */ missed: readonly { id: string; index: number; amount: number; }[]; /** Loans that reached the miss limit during this advance. */ defaulted: readonly string[]; } /** Counters of a loan book, for a probe or a summary line. */ export interface LoanBookStats { loans: number; active: number; defaulted: number; settled: number; /** Remaining principal across every loan, in minor units. */ outstanding: number; paidAmount: number; interestPaid: number; missed: number; } /** Outcome of an early repayment. */ export interface LoanRepayment { ok: boolean; /** Minor units actually applied to principal. */ applied: number; outstanding: number; settled: boolean; } /** Options for {@link createLoanBook}. */ export interface LoanBookOptions { /** The books every draw, payment and repayment is posted through. */ ledger: TransactionLedger; /** Default currency for loans that do not name one. */ currency?: string; /** Default owing account for loans that do not name one. */ account?: string; /** Due payments that may go unsettled before a loan is defaulted (default `3`). */ maxMissed?: number; /** Register a devtools probe under this name reporting {@link LoanBookStats}. */ devtoolsName?: string; } /** The loans a business owes: schedules, interest, arrears and default, posted to a ledger. */ export interface LoanBook { /** Draw a loan: posts the principal to the borrower's account and schedules the repayments. `null` for unusable terms or an unknown account. */ originate(terms: LoanTerms): LoanRecord | null; /** Settle every payment due at or before `nowSeconds`, in loan-id order. */ advance(nowSeconds: number): LoanAdvanceResult; /** Pay principal early. Interest already scheduled is unaffected. */ repay(loanId: string, amount: number, atSeconds: number): LoanRepayment | null; outstanding(loanId: string): number; /** The next payment due, or `null` when the loan is settled or paid off. */ nextPayment(loanId: string): LoanPayment | null; scheduleOf(loanId: string): LoanSchedule | null; loan(loanId: string): LoanRecord | null; loans(): readonly LoanRecord[]; snapshot(): LoanBookState; /** Replace state; returns `false` without mutation when the state is malformed. */ restore(next: LoanBookState): boolean; stats(): LoanBookStats; reset(): void; /** Drop the devtools probe registered by `devtoolsName`. Safe to call twice. */ dispose(): void; } /** * Build a loan's repayment schedule: one entry per period with its due time, interest/principal * split and the balance left, in integer minor units. An amortized loan levels the payment over * the outstanding balance (`P·i / (1 − (1+i)^−n)`); a flat loan charges interest on the original * principal. Either way the final payment absorbs the rounding, so the principal parts sum to the * principal exactly and the balance ends at zero. Deterministic and pure. * * @capability loan-schedule build a loan's interest and principal repayment schedule in integer minor units */ export declare function buildLoanSchedule(terms: LoanTerms): LoanSchedule; /** * The loans a business owes, over a {@link TransactionLedger}: each draw posts the principal to the * borrower's account, each settled payment posts its interest (`"loan-interest"`) and principal * (`"loan-principal"`) as separate reason-tagged entries, and an early repayment posts * `"loan-repayment"`. A payment that comes due with no funds is left in arrears and counted once * per advance; after `maxMissed` the loan is defaulted and stops being advanced. State is plain * data — `snapshot()` stores terms and progress, and the schedule is rebuilt from the terms. * * @capability loans draw, schedule, service and default a loan with interest, posted to a ledger */ export declare function createLoanBook(options: LoanBookOptions): LoanBook;