adamsaghy commented on code in PR #5771:
URL: https://github.com/apache/fineract/pull/5771#discussion_r3187578868
##########
fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/calc/ProjectedAmortizationScheduleModel.java:
##########
@@ -247,6 +270,99 @@ public void
recalculateNetAmortizationAndDeferredBalanceFrom(final LocalDate rep
this.payments = List.copyOf(adjusted);
}
+ /**
+ * Applies a rate change at the given date. Adds a {@link RateSegment}
covering the remaining term from the change
+ * date forward. The model is mutated in-place; the payment list is
rebuilt.
+ *
+ * <p>
+ * Any existing segments at or after the split point are removed first
(supports undo/overwrite).
+ *
+ * @param newPeriodPaymentRate
+ * the new period payment rate
+ * @param rateChangeDate
+ * the date of the rate change (must be within model's date
range)
+ */
+ public void applyRateChange(final BigDecimal newPeriodPaymentRate, final
LocalDate rateChangeDate) {
+ Objects.requireNonNull(newPeriodPaymentRate, "newPeriodPaymentRate");
+ Objects.requireNonNull(rateChangeDate, "rateChangeDate");
+
+ final int rawSplitDayIndex = (int)
ChronoUnit.DAYS.between(expectedDisbursementDate, rateChangeDate);
+ if (rawSplitDayIndex < 0) {
+ throw new IllegalArgumentException("rateChangeDate must not be
before expectedDisbursementDate");
+ }
+
+ // When the rate change is past the base schedule's term, clamp the
segment start
+ // to loanTerm. The loan is still active (borrower hasn't paid), so
the remaining
+ // balance is netDisbursement - paymentsReceived.
+ final int splitDayIndex = Math.min(rawSplitDayIndex, loanTerm);
+
+ // Remove existing segments at or after split (supports overwrite on
second rate change)
+ // Guard against null rateSegments from V1 model deserialization
+ if (rateSegments == null) {
+ throw new IllegalStateException("Model not properly initialized;
rateSegments is null");
+ }
+ rateSegments.removeIf(s -> s.startDayIndex() >= splitDayIndex);
+
+ // Collect actual payments received before the split
+ BigDecimal paymentsReceived = BigDecimal.ZERO;
+ for (final ProjectedPayment p : payments) {
+ if (p.paymentNo() <= 0 || p.paymentNo() > splitDayIndex) {
+ continue;
+ }
+ if (p.actualPaymentAmount() != null) {
+ paymentsReceived =
paymentsReceived.add(p.actualPaymentAmount().getAmount(), mc);
+ }
+ }
+
+ // Compute balance at split: if past term, use remaining principal;
otherwise use base amortization
+ final BigDecimal balanceAtSplit;
+ if (rawSplitDayIndex >= loanTerm) {
+ balanceAtSplit =
netDisbursementAmount.getAmount().subtract(paymentsReceived, mc);
+ } else if (splitDayIndex > 0) {
+ final BalancesAndAmortizations ba =
computeBaseBalancesUpTo(splitDayIndex);
+ balanceAtSplit = ba.balances().get(splitDayIndex - 1);
+ } else {
+ balanceAtSplit = netDisbursementAmount.getAmount();
+ }
+
+ final BigDecimal origNet = netDisbursementAmount.getAmount();
+ final BigDecimal origDiscount = originationFeeAmount.getAmount();
+ final BigDecimal tpv = totalPaymentValue.getAmount();
+
+ final BigDecimal newNetDisb = balanceAtSplit;
+ final BigDecimal newDiscount = origDiscount.add(origNet,
mc).subtract(balanceAtSplit, mc).subtract(paymentsReceived, mc);
+ final int scale = currency.getDigitsAfterDecimal();
+ final BigDecimal newDailyPayment = tpv.multiply(newPeriodPaymentRate,
mc).divide(BigDecimal.valueOf(npvDayCount), mc)
+ .setScale(scale, RoundingMode.HALF_UP);
Review Comment:
Hardcoded rounding mode still....
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]