Cache the sfPaymentRemaining proxy once instead of re-fetching it at
each of the three places it's read in the loop condition, the final-
payment assert, and the overpayment guard. It's a write-through proxy,
so it still reflects doPayment's mutations each iteration.
Also explicitly zero-initialize totalPaid so the starting baseline
isn't left implicit.
Extract loanRatesFor() to replace the identical 4-line
managementFeeRate/interestRate/periodicRate preamble repeated in
makeFullPayment, makeLatePayment, and makeRegularPayment.
Clarify the constructLoanState(SLE::const_ref) doc comment to note
it's an overload of the 3-Number constructLoanState() above it,
rather than reading as a standalone function.
loanMakePayment mixed shared guard checks with the full logic for
each of the four payment types in one large function. Split the
type-specific logic into makeFullPayment, makeLatePayment, and
makeRegularPayment (covering both Regular and Overpayment, which
share the same amortization loop), leaving loanMakePayment as a thin
dispatcher: run the shared guards, then switch on paymentType.
Each new function derives the loan-scale/periodic-rate/management-fee
values it needs from the Loan and LoanBroker SLEs directly rather than
having them threaded in, consistent with the rest of this refactor.
Extract roundAndSplitInterest to replace three identical inline
lambdas in computeLatePayment, computeFullPayment, and
computeOverpaymentComponents that round raw interest and split it
into net interest and management fee.
Also drop the UNREACHABLE fallback in loanMakePayment's Full and Late
branches: computeFullPayment and computeLatePayment only ever fail
with a genuine error TER, never tesSUCCESS, so the "no-op" outcome
that pattern guards against (needed for the Overpayment path, where
it's real) can't happen here.
doPayment, doOverpayment, computeFullPayment, and computeLatePayment
each took a long list of individually-fetched proxy/value parameters
that were always sourced from the same Loan ledger object at their
single call site. Take SLE::ref/const_ref directly instead and unwrap
fields internally, cutting parameter counts and removing threading
duplication in loanMakePayment. computePaymentComponents gains a thin
SLE-unwrapping overload alongside its existing value-based form, which
stays for direct exercise by the unit test model-based checks.
Also renames constructRoundedLoanState to a constructLoanState
overload for consistency with the other overload.
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>