Skip to content

Commit 1f17aed

Browse files
committed
FINERACT-2326: Improve null-safety
1 parent 53d9790 commit 1f17aed

File tree

6 files changed

+468
-128
lines changed

6 files changed

+468
-128
lines changed

fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/MathUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,10 @@ public static Money nullToZero(Money value, @NotNull MonetaryCurrency currency)
335335
return nullToDefault(value, Money.zero(currency));
336336
}
337337

338+
public static Money nullToZero(Money value, @NotNull MonetaryCurrency currency, @NotNull MathContext mc) {
339+
return nullToDefault(value, Money.zero(currency, mc));
340+
}
341+
338342
public static Money nullToDefault(Money value, Money def) {
339343
return value == null ? def : value;
340344
}

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ public void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleM
125125
}
126126

127127
private void addDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
128-
scheduleModel.repaymentPeriods().stream().filter(rp -> !operation.getSubmittedOnDate().isAfter(rp.getFromDate())).forEach(
129-
rp -> rp.setTotalDisbursedAmount(MathUtil.nullToZero(rp.getTotalDisbursedAmount()).add(operation.getAmount().getAmount())));
128+
scheduleModel.repaymentPeriods().stream().filter(rp -> !operation.getSubmittedOnDate().isAfter(rp.getFromDate()))
129+
.forEach(rp -> rp.setTotalDisbursedAmount(rp.getTotalDisbursedAmount().add(operation.getAmount())));
130130

131131
scheduleModel
132132
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
@@ -162,8 +162,7 @@ public void addCapitalizedIncome(final ProgressiveLoanInterestScheduleModel sche
162162

163163
private void addCapitalizedIncome(final ProgressiveLoanInterestScheduleModel scheduleModel, final EmiChangeOperation operation) {
164164
scheduleModel.repaymentPeriods().stream().filter(rp -> !operation.getSubmittedOnDate().isAfter(rp.getFromDate()))
165-
.forEach(rp -> rp.setTotalCapitalizedIncomeAmount(
166-
MathUtil.nullToZero(rp.getTotalCapitalizedIncomeAmount()).add(operation.getAmount().getAmount())));
165+
.forEach(rp -> rp.setTotalCapitalizedIncomeAmount(rp.getTotalCapitalizedIncomeAmount().plus(operation.getAmount())));
167166

168167
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), scheduleModel.zero(),
169168
scheduleModel.zero(), operation.getAmount()).ifPresent((repaymentPeriod) -> {
@@ -225,7 +224,6 @@ public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, Loc
225224
return;
226225
}
227226
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate);
228-
boolean transactionDateIsBefore = transactionDate.isBefore(repaymentPeriod.get().getFromDate());
229227
repaymentPeriod.ifPresent(rp -> rp.addPaidPrincipalAmount(principalAmount));
230228
// If it is paid late, we need to calculate with the period due date
231229
LocalDate balanceCorrectionDate = DateUtils.isBefore(repaymentPeriodDueDate, transactionDate) ? repaymentPeriodDueDate
@@ -235,6 +233,7 @@ public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, Loc
235233
repaymentPeriod.ifPresent(rp -> {
236234
// If any period total paid > calculated EMI, then set EMI to total paid -> effectively it is marked as
237235
// fully paid
236+
boolean transactionDateIsBefore = transactionDate.isBefore(repaymentPeriod.get().getFromDate());
238237
if (transactionDateIsBefore
239238
&& rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest())) {
240239
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
@@ -368,7 +367,7 @@ public Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleMo
368367
return recalculatedScheduleModelTillDate.getLastRepaymentPeriod();
369368
} else {
370369
// if target date is before 1st disbursement date, we use 1st repayment period
371-
return recalculatedScheduleModelTillDate.repaymentPeriods().get(0);
370+
return recalculatedScheduleModelTillDate.repaymentPeriods().getFirst();
372371
}
373372
});
374373

@@ -406,7 +405,7 @@ private ProgressiveLoanInterestScheduleModel recalculateScheduleModelTillDate(
406405
@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate targetDate) {
407406
MathContext mc = scheduleModel.mc();
408407
ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.deepCopy(mc);
409-
boolean isBeforeFirstDisbursement = targetDate.isBefore(scheduleModelCopy.repaymentPeriods().get(0).getFromDate());
408+
boolean isBeforeFirstDisbursement = targetDate.isBefore(scheduleModelCopy.repaymentPeriods().getFirst().getFromDate());
410409
boolean isAfterMaturityDate = !targetDate.isBefore(scheduleModelCopy.getLastRepaymentPeriod().getDueDate());
411410
if (isBeforeFirstDisbursement) {
412411
scheduleModelCopy.repaymentPeriods().forEach(rp -> rp.getInterestPeriods().clear());
@@ -436,13 +435,13 @@ private ProgressiveLoanInterestScheduleModel recalculateScheduleModelTillDate(
436435
.subList(nextIdx, ip.getRepaymentPeriod().getInterestPeriods().size()).clear();
437436
});
438437
} else if (rp.getPrevious().isPresent() && rp.getPrevious().get().equals(repaymentPeriod)
439-
&& (rp.getInterestPeriods().get(0).getCreditedInterest().isGreaterThanZero()
440-
|| rp.getInterestPeriods().get(0).getCreditedPrincipal().isGreaterThanZero())) {
438+
&& (rp.getInterestPeriods().getFirst().getCreditedInterest().isGreaterThanZero()
439+
|| rp.getInterestPeriods().getFirst().getCreditedPrincipal().isGreaterThanZero())) {
441440
// NOTE: we need to check whether there is credited on the 1st interest period of the next
442441
// period
443442
// if so, we need to retain that interest period, but need to update due date to match with from
444443
// date -> 0 interest
445-
rp.getInterestPeriods().get(0).setDueDate(rp.getInterestPeriods().get(0).getFromDate());
444+
rp.getInterestPeriods().getFirst().setDueDate(rp.getInterestPeriods().getFirst().getFromDate());
446445
if (rp.getInterestPeriods().size() > 1) {
447446
rp.getInterestPeriods().subList(1, rp.getInterestPeriods().size()).clear();
448447
}
@@ -580,7 +579,7 @@ private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final Progressiv
580579
if (newScheduleModel == null) {
581580
newScheduleModel = scheduleModel.deepCopy(mc);
582581
}
583-
final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.get(0).getDueDate();
582+
final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.getFirst().getDueDate();
584583
newScheduleModel.repaymentPeriods().forEach(period -> {
585584
if (!period.getDueDate().isBefore(relatedPeriodsFirstDueDate)
586585
&& !adjustedEqualMonthlyInstallmentValue.isLessThan(period.getTotalPaidAmount())) {
@@ -913,8 +912,8 @@ private void calculateEMIOnActualModelWithFlatInterestMethod(List<RepaymentPerio
913912

914913
final MathContext mc = scheduleModel.mc();
915914
final CurrencyData currency = scheduleModel.loanProductRelatedDetail().getCurrencyData();
916-
RepaymentPeriod first = repaymentPeriods.getFirst();
917-
RepaymentPeriod last = repaymentPeriods.getLast();
915+
RepaymentPeriod firstRepaymentPeriod = repaymentPeriods.getFirst();
916+
RepaymentPeriod lastRepaymentPeriod = repaymentPeriods.getLast();
918917
Money sumOfInterest = Money.zero(currency);
919918
for (RepaymentPeriod rp : repaymentPeriods) {
920919
Money interest = rp.calculateCalculatedDueInterest();
@@ -923,20 +922,19 @@ private void calculateEMIOnActualModelWithFlatInterestMethod(List<RepaymentPerio
923922
}
924923

925924
// already repaid principals should be subtracted from total disbursed amount to calculate correct EMI.
926-
BigDecimal alreadyRepaidPrincipals = first.getPrevious()
927-
.map(rp -> rp.calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(null)
928-
.subtract(rp.getOutstandingLoanBalance().getAmount()))
929-
.orElse(BigDecimal.ZERO);
930-
Money total = Money
931-
.of(currency, first.calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(first.getLastInterestPeriod()))
925+
Money alreadyRepaidPrincipals = firstRepaymentPeriod.getPrevious()
926+
.map(rp -> rp.calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(null).minus(rp.getOutstandingLoanBalance()))
927+
.orElse(null);
928+
Money total = firstRepaymentPeriod
929+
.calculateTotalDisbursedAndCapitalizedIncomeAmountTillGivenPeriod(firstRepaymentPeriod.getLastInterestPeriod())
932930
.plus(sumOfInterest).minus(alreadyRepaidPrincipals);
933931

934932
Money periodEmi = total.dividedBy(repaymentPeriods.size(), mc);
935933
Money periodEmiInMultiplesOf = applyInstallmentAmountInMultiplesOf(scheduleModel, periodEmi);
936934
Money remainder = total.minus(periodEmiInMultiplesOf.multipliedBy(repaymentPeriods.size(), mc));
937935

938936
repaymentPeriods.forEach(rp -> {
939-
Money emi = rp.equals(last) ? periodEmiInMultiplesOf.add(remainder) : periodEmiInMultiplesOf;
937+
Money emi = rp.equals(lastRepaymentPeriod) ? periodEmiInMultiplesOf.add(remainder) : periodEmiInMultiplesOf;
940938
rp.setEmi(emi);
941939
rp.setOriginalEmi(emi);
942940
rp.getInterestPeriods().forEach(InterestPeriod::updateOutstandingLoanBalance);
@@ -959,7 +957,7 @@ private void calculateEMIOnActualModelWithDecliningBalanceInterestMethod(List<Re
959957
final MathContext mc = scheduleModel.mc();
960958
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods, mc));
961959
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods, mc));
962-
final RepaymentPeriod startPeriod = repaymentPeriods.get(0);
960+
final RepaymentPeriod startPeriod = repaymentPeriods.getFirst();
963961

964962
final Money outstandingBalance = startPeriod.getInitialBalanceForEmiRecalculation();
965963

@@ -985,7 +983,7 @@ private void calculateEMIOnNewModelAndMerge(List<RepaymentPeriod> repaymentPerio
985983
addDisbursement(scheduleModelCopy, operation.withZeroAmount());
986984
addCapitalizedIncome(scheduleModelCopy, operation.withZeroAmount());
987985

988-
final LocalDate firstDueDate = repaymentPeriods.get(0).getDueDate();
986+
final LocalDate firstDueDate = repaymentPeriods.getFirst().getDueDate();
989987
scheduleModel.copyPeriodsFrom(firstDueDate, scheduleModelCopy.repaymentPeriods(), (newRepaymentPeriod, actualRepaymentPeriod) -> {
990988
actualRepaymentPeriod.setEmi(newRepaymentPeriod.getEmi());
991989
actualRepaymentPeriod.setOriginalEmi(newRepaymentPeriod.getOriginalEmi());
@@ -1009,7 +1007,7 @@ public EmiAdjustment getEmiAdjustment(final List<RepaymentPeriod> repaymentPerio
10091007
getUncountablePeriods(repaymentPeriods, penultimatePeriod.getEmi()));
10101008
}
10111009
}
1012-
return new EmiAdjustment(repaymentPeriods.get(0).getEmi(), repaymentPeriods.get(0).getEmi().copy(0.0), repaymentPeriods, 0);
1010+
return new EmiAdjustment(repaymentPeriods.getFirst().getEmi(), repaymentPeriods.getFirst().getEmi().copy(0.0), repaymentPeriods, 0);
10131011
}
10141012

10151013
private void calculateRateFactorForScheduleTillDateInclusive(ProgressiveLoanInterestScheduleModel scheduleModelCopy,

0 commit comments

Comments
 (0)