FINERACT-2190: Avoid recreation of loan repayment schedule periods

This commit is contained in:
Jose Alberto Hernandez
2025-04-04 14:29:10 +02:00
committed by Adam Saghy
parent b9cfa470ff
commit 52c41b1471
13 changed files with 229 additions and 117 deletions
@@ -57,7 +57,6 @@ import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -112,7 +111,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Loa
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType; import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
@@ -610,8 +608,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate(); this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
this.submittedOnDate = submittedOnDate != null ? submittedOnDate : DateUtils.getBusinessLocalDate(); this.submittedOnDate = submittedOnDate != null ? submittedOnDate : DateUtils.getBusinessLocalDate();
updateLoanSchedule(loanScheduleModel);
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
// Copy interest recalculation settings if interest recalculation is enabled // Copy interest recalculation settings if interest recalculation is enabled
@@ -1005,53 +1001,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
this.rates.addAll(loanRates); this.rates.addAll(loanRates);
} }
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
this.repaymentScheduleInstallments.clear();
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
if (scheduledLoanInstallment.isRepaymentPeriod() || scheduledLoanInstallment.isDownPaymentPeriod()) {
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this,
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(),
scheduledLoanInstallment.getLoanCompoundingDetails(), scheduledLoanInstallment.rescheduleInterestPortion(),
scheduledLoanInstallment.isDownPaymentPeriod());
addLoanRepaymentScheduleInstallment(installment);
}
}
updateLoanScheduleDependentDerivedFields();
updateLoanSummaryDerivedFields();
}
public void updateLoanSchedule(final Collection<LoanRepaymentScheduleInstallment> installments) {
List<LoanRepaymentScheduleInstallment> existingInstallments = new ArrayList<>(this.repaymentScheduleInstallments);
repaymentScheduleInstallments.clear();
for (final LoanRepaymentScheduleInstallment installment : installments) {
LoanRepaymentScheduleInstallment existingInstallment = findByInstallmentNumber(existingInstallments,
installment.getInstallmentNumber());
if (existingInstallment != null) {
Set<LoanInstallmentCharge> existingCharges = existingInstallment.getInstallmentCharges();
installment.getInstallmentCharges().addAll(existingCharges);
existingCharges.forEach(c -> c.setInstallment(installment));
existingInstallment.getInstallmentCharges().clear();
}
addLoanRepaymentScheduleInstallment(installment);
}
updateLoanScheduleDependentDerivedFields();
updateLoanSummaryDerivedFields();
}
private LoanRepaymentScheduleInstallment findByInstallmentNumber(Collection<LoanRepaymentScheduleInstallment> installments,
Integer installmentNumber) {
for (LoanRepaymentScheduleInstallment installment : installments) {
if (Objects.equals(installment.getInstallmentNumber(), installmentNumber)) {
return installment;
}
}
return null;
}
public void updateLoanScheduleDependentDerivedFields() { public void updateLoanScheduleDependentDerivedFields() {
if (this.getLoanRepaymentScheduleInstallmentsSize() > 0) { if (this.getLoanRepaymentScheduleInstallmentsSize() > 0) {
this.expectedMaturityDate = determineExpectedMaturityDate(); this.expectedMaturityDate = determineExpectedMaturityDate();
@@ -38,10 +38,12 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType; import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks; import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
@Getter @Getter
@Setter
@Entity @Entity
@Table(name = "m_loan_repayment_schedule") @Table(name = "m_loan_repayment_schedule")
public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom<Long> public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom<Long>
@@ -60,67 +62,67 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
@Column(name = "duedate", nullable = false) @Column(name = "duedate", nullable = false)
private LocalDate dueDate; private LocalDate dueDate;
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true) @Column(name = "principal_amount", scale = 6, precision = 19)
private BigDecimal principal; private BigDecimal principal;
@Column(name = "principal_completed_derived", scale = 6, precision = 19, nullable = true) @Column(name = "principal_completed_derived", scale = 6, precision = 19)
private BigDecimal principalCompleted; private BigDecimal principalCompleted;
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19, nullable = true) @Column(name = "principal_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal principalWrittenOff; private BigDecimal principalWrittenOff;
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true) @Column(name = "interest_amount", scale = 6, precision = 19)
private BigDecimal interestCharged; private BigDecimal interestCharged;
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true) @Column(name = "interest_completed_derived", scale = 6, precision = 19)
private BigDecimal interestPaid; private BigDecimal interestPaid;
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true) @Column(name = "interest_waived_derived", scale = 6, precision = 19)
private BigDecimal interestWaived; private BigDecimal interestWaived;
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true) @Column(name = "interest_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal interestWrittenOff; private BigDecimal interestWrittenOff;
@Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true) @Column(name = "accrual_interest_derived", scale = 6, precision = 19)
private BigDecimal interestAccrued; private BigDecimal interestAccrued;
@Column(name = "reschedule_interest_portion", scale = 6, precision = 19, nullable = true) @Column(name = "reschedule_interest_portion", scale = 6, precision = 19)
private BigDecimal rescheduleInterestPortion; private BigDecimal rescheduleInterestPortion;
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true) @Column(name = "fee_charges_amount", scale = 6, precision = 19)
private BigDecimal feeChargesCharged; private BigDecimal feeChargesCharged;
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19, nullable = true) @Column(name = "fee_charges_completed_derived", scale = 6, precision = 19)
private BigDecimal feeChargesPaid; private BigDecimal feeChargesPaid;
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true) @Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal feeChargesWrittenOff; private BigDecimal feeChargesWrittenOff;
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true) @Column(name = "fee_charges_waived_derived", scale = 6, precision = 19)
private BigDecimal feeChargesWaived; private BigDecimal feeChargesWaived;
@Column(name = "accrual_fee_charges_derived", scale = 6, precision = 19, nullable = true) @Column(name = "accrual_fee_charges_derived", scale = 6, precision = 19)
private BigDecimal feeAccrued; private BigDecimal feeAccrued;
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true) @Column(name = "penalty_charges_amount", scale = 6, precision = 19)
private BigDecimal penaltyCharges; private BigDecimal penaltyCharges;
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19, nullable = true) @Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19)
private BigDecimal penaltyChargesPaid; private BigDecimal penaltyChargesPaid;
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true) @Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19)
private BigDecimal penaltyChargesWrittenOff; private BigDecimal penaltyChargesWrittenOff;
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true) @Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19)
private BigDecimal penaltyChargesWaived; private BigDecimal penaltyChargesWaived;
@Column(name = "accrual_penalty_charges_derived", scale = 6, precision = 19, nullable = true) @Column(name = "accrual_penalty_charges_derived", scale = 6, precision = 19)
private BigDecimal penaltyAccrued; private BigDecimal penaltyAccrued;
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19, nullable = true) @Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19)
private BigDecimal totalPaidInAdvance; private BigDecimal totalPaidInAdvance;
@Column(name = "total_paid_late_derived", scale = 6, precision = 19, nullable = true) @Column(name = "total_paid_late_derived", scale = 6, precision = 19)
private BigDecimal totalPaidLate; private BigDecimal totalPaidLate;
@Column(name = "completed_derived", nullable = false) @Column(name = "completed_derived", nullable = false)
@@ -139,17 +141,17 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
// approach // approach
// https://blog.thepete.net/blog/2023/12/05/expand/contract-making-a-breaking-change-without-a-big-bang/ // https://blog.thepete.net/blog/2023/12/05/expand/contract-making-a-breaking-change-without-a-big-bang/
@Setter @Setter
@Column(name = "credits_amount", scale = 6, precision = 19, nullable = true) @Column(name = "credits_amount", scale = 6, precision = 19)
private BigDecimal creditedPrincipal; private BigDecimal creditedPrincipal;
@Setter @Setter
@Column(name = "credited_interest", scale = 6, precision = 19, nullable = true) @Column(name = "credited_interest", scale = 6, precision = 19)
private BigDecimal creditedInterest; private BigDecimal creditedInterest;
@Column(name = "credited_fee", scale = 6, precision = 19, nullable = true) @Column(name = "credited_fee", scale = 6, precision = 19)
private BigDecimal creditedFee; private BigDecimal creditedFee;
@Column(name = "credited_penalty", scale = 6, precision = 19, nullable = true) @Column(name = "credited_penalty", scale = 6, precision = 19)
private BigDecimal creditedPenalty; private BigDecimal creditedPenalty;
@Column(name = "is_down_payment", nullable = false) @Column(name = "is_down_payment", nullable = false)
@@ -1068,4 +1070,58 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
MathUtil.nullToZero(MathUtil.add(getPrincipal(), getInterestCharged(), getFeeChargesCharged(), getPenaltyCharges()))); MathUtil.nullToZero(MathUtil.add(getPrincipal(), getInterestCharged(), getFeeChargesCharged(), getPenaltyCharges())));
} }
public void copyFrom(final LoanScheduleModelPeriod period) {
resetBalances();
setFromDate(period.periodFromDate());
setDueDate(period.periodDueDate());
setPrincipal(period.principalDue());
setInterestCharged(period.interestDue());
setFeeChargesCharged(period.feeChargesDue());
setPenaltyCharges(period.penaltyChargesDue());
setRecalculatedInterestComponent(period.isRecalculatedInterestComponent());
if (getLoanCompoundingDetails() != null) {
getLoanCompoundingDetails().clear();
}
if (period.getLoanCompoundingDetails() != null) {
period.getLoanCompoundingDetails().forEach(lcd -> {
lcd.setLoanRepaymentScheduleInstallment(this);
getLoanCompoundingDetails().add(lcd);
});
}
setRescheduleInterestPortion(period.rescheduleInterestPortion());
setDownPayment(period.isDownPaymentPeriod());
setAdditional(false);
setReAged(false);
}
public void copyFrom(final LoanRepaymentScheduleInstallment installment) {
setFromDate(installment.getFromDate());
setDueDate(installment.getDueDate());
setPrincipal(installment.getPrincipal());
setInterestCharged(installment.getInterestCharged());
setFeeChargesCharged(installment.getFeeChargesCharged());
setPenaltyCharges(installment.getPenaltyCharges());
setRecalculatedInterestComponent(installment.isRecalculatedInterestComponent());
if (getLoanCompoundingDetails() != null) {
getLoanCompoundingDetails().clear();
}
if (installment.getLoanCompoundingDetails() != null) {
installment.getLoanCompoundingDetails().forEach(lcd -> {
lcd.setLoanRepaymentScheduleInstallment(this);
getLoanCompoundingDetails().add(lcd);
});
}
Set<LoanInstallmentCharge> installmentCharges = installment.getInstallmentCharges();
installmentCharges.forEach(c -> c.setInstallment(this));
getInstallmentCharges().addAll(installmentCharges);
setAdditional(installment.isAdditional());
setReAged(installment.isReAged());
setDownPayment(installment.isDownPayment());
}
} }
@@ -29,6 +29,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
@RequiredArgsConstructor @RequiredArgsConstructor
public class LoanScheduleService { public class LoanScheduleService {
@@ -36,16 +37,17 @@ public class LoanScheduleService {
private final LoanChargeService loanChargeService; private final LoanChargeService loanChargeService;
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
private final LoanTransactionProcessingService loadTransactionProcessingService; private final LoanTransactionProcessingService loadTransactionProcessingService;
private final LoanScheduleComponent loanSchedule;
/** /**
* Ability to regenerate the repayment schedule based on the loans current details/state. * Ability to regenerate the repayment schedule based on the loans current details/state.
*/ */
public void regenerateRepaymentSchedule(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) { public void regenerateRepaymentSchedule(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final LoanScheduleModel loanSchedule = loan.regenerateScheduleModel(scheduleGeneratorDTO); final LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO);
if (loanSchedule == null) { if (loanScheduleModel == null) {
return; return;
} }
loan.updateLoanSchedule(loanSchedule); loanSchedule.updateLoanSchedule(loan, loanScheduleModel);
final Set<LoanCharge> charges = loan.getActiveCharges(); final Set<LoanCharge> charges = loan.getActiveCharges();
for (final LoanCharge loanCharge : charges) { for (final LoanCharge loanCharge : charges) {
if (!loanCharge.isWaived()) { if (!loanCharge.isWaived()) {
@@ -80,15 +82,15 @@ public class LoanScheduleService {
public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) { public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) {
final LocalDate lastTransactionDate = loan.getLastUserTransactionDate(); final LocalDate lastTransactionDate = loan.getLastUserTransactionDate();
final LoanScheduleDTO loanSchedule = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan); final LoanScheduleDTO loanScheduleDTO = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan);
if (loanSchedule == null) { if (loanScheduleDTO == null) {
return; return;
} }
// Either the installments got recalculated or the model // Either the installments got recalculated or the model
if (loanSchedule.getInstallments() != null) { if (loanScheduleDTO.getInstallments() != null) {
loan.updateLoanSchedule(loanSchedule.getInstallments()); loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getInstallments());
} else { } else {
loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel()); loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel());
} }
loan.setInterestRecalculatedOn(DateUtils.getBusinessLocalDate()); loan.setInterestRecalculatedOn(DateUtils.getBusinessLocalDate());
final LocalDate lastRepaymentDate = loan.getLastRepaymentPeriodDueDate(true); final LocalDate lastRepaymentDate = loan.getLastRepaymentPeriodDueDate(true);
@@ -0,0 +1,91 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.service.schedule;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class LoanScheduleComponent {
public void updateLoanSchedule(Loan loan, final LoanScheduleModel modifiedLoanSchedule) {
final List<LoanScheduleModelPeriod> periods = modifiedLoanSchedule.getPeriods();
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
if (scheduledLoanInstallment.isRepaymentPeriod() || scheduledLoanInstallment.isDownPaymentPeriod()) {
LoanRepaymentScheduleInstallment existingInstallment = findByInstallmentNumber(loan.getRepaymentScheduleInstallments(),
scheduledLoanInstallment.periodNumber());
if (existingInstallment == null) {
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan,
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(),
scheduledLoanInstallment.getLoanCompoundingDetails(), scheduledLoanInstallment.rescheduleInterestPortion(),
scheduledLoanInstallment.isDownPaymentPeriod());
loan.addLoanRepaymentScheduleInstallment(installment);
} else {
existingInstallment.copyFrom(scheduledLoanInstallment);
}
}
}
// Review Installments removed
loan.getRepaymentScheduleInstallments().removeIf(i -> !existInstallment(periods, i.getInstallmentNumber()));
loan.updateLoanScheduleDependentDerivedFields();
loan.updateLoanSummaryDerivedFields();
}
public void updateLoanSchedule(Loan loan, final List<LoanRepaymentScheduleInstallment> installments) {
for (final LoanRepaymentScheduleInstallment installment : installments) {
LoanRepaymentScheduleInstallment existingInstallment = findByInstallmentNumber(loan.getRepaymentScheduleInstallments(),
installment.getInstallmentNumber());
if (existingInstallment != null) {
existingInstallment.copyFrom(installment);
} else {
loan.addLoanRepaymentScheduleInstallment(installment);
}
}
// Review Installments removed
loan.getRepaymentScheduleInstallments().removeIf(i -> !existInstallment(installments, i.getInstallmentNumber()));
loan.updateLoanScheduleDependentDerivedFields();
loan.updateLoanSummaryDerivedFields();
}
private LoanRepaymentScheduleInstallment findByInstallmentNumber(final Collection<LoanRepaymentScheduleInstallment> installments,
final Integer installmentNumber) {
return installments.stream().filter(i -> installmentNumber.compareTo(i.getInstallmentNumber()) == 0).findFirst().orElse(null);
}
private boolean existInstallment(final Collection<LoanRepaymentScheduleInstallment> installments, final Integer installmentNumber) {
return installments.stream().anyMatch(i -> installmentNumber.compareTo(i.getInstallmentNumber()) == 0);
}
private boolean existInstallment(final List<LoanScheduleModelPeriod> periods, final Integer installmentNumber) {
return periods.stream().anyMatch(p -> p.periodNumber() != null && installmentNumber.compareTo(p.periodNumber()) == 0);
}
}
@@ -91,6 +91,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService; import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails;
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
@@ -114,13 +115,15 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private final EMICalculator emiCalculator; private final EMICalculator emiCalculator;
private final LoanRepositoryWrapper loanRepositoryWrapper; private final LoanRepositoryWrapper loanRepositoryWrapper;
private final InterestRefundService interestRefundService; private final InterestRefundService interestRefundService;
private final LoanScheduleComponent loanSchedule;
public AdvancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper, public AdvancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper,
InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory) { InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory, LoanScheduleComponent loanSchedule) {
super(externalIdFactory); super(externalIdFactory);
this.emiCalculator = emiCalculator; this.emiCalculator = emiCalculator;
this.loanRepositoryWrapper = loanRepositoryWrapper; this.loanRepositoryWrapper = loanRepositoryWrapper;
this.interestRefundService = interestRefundService; this.interestRefundService = interestRefundService;
this.loanSchedule = loanSchedule;
} }
@Override @Override
@@ -1460,8 +1463,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
} }
} }
loan.updateLoanSchedule(installmentsUpToTransactionDate); loanSchedule.updateLoanSchedule(loan, installmentsUpToTransactionDate);
loan.updateLoanScheduleDependentDerivedFields();
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && loan.isInterestRecalculationEnabled()) { if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && loan.isInterestRecalculationEnabled()) {
updateRepaymentPeriodsAfterChargeOff(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed); updateRepaymentPeriodsAfterChargeOff(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed);
@@ -69,6 +69,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
import org.apache.fineract.portfolio.loanproduct.domain.AllocationType; import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
@@ -98,6 +99,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
private AdvancedPaymentScheduleTransactionProcessor underTest; private AdvancedPaymentScheduleTransactionProcessor underTest;
private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class); private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
private static final LoanRepositoryWrapper loanRepositoryWrapper = Mockito.mock(LoanRepositoryWrapper.class); private static final LoanRepositoryWrapper loanRepositoryWrapper = Mockito.mock(LoanRepositoryWrapper.class);
private static final LoanScheduleComponent loanSchedule = Mockito.mock(LoanScheduleComponent.class);
@BeforeAll @BeforeAll
public static void init() { public static void init() {
@@ -112,7 +114,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, null, null); underTest = new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, null, null, loanSchedule);
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT); ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
@@ -79,6 +79,7 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService; import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService; import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -115,6 +116,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private final LoanAccrualsProcessingService loanAccrualsProcessingService; private final LoanAccrualsProcessingService loanAccrualsProcessingService;
private final LoanChargeService loanChargeService; private final LoanChargeService loanChargeService;
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
private final LoanScheduleComponent loanSchedule;
/** /**
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist * create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
@@ -423,14 +425,14 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
loanApplicationTerms.getInterestMethod()); loanApplicationTerms.getInterestMethod());
final LoanLifecycleStateMachine loanLifecycleStateMachine = null; final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
loan.setHelpers(loanLifecycleStateMachine); loan.setHelpers(loanLifecycleStateMachine);
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan, final LoanScheduleDTO loanScheduleDTO = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate); loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
// Either the installments got recalculated or the model // Either the installments got recalculated or the model
if (loanSchedule.getInstallments() != null) { if (loanScheduleDTO.getInstallments() != null) {
loan.updateLoanSchedule(loanSchedule.getInstallments()); loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getInstallments());
} else { } else {
loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel()); loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel());
} }
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanChargeService.recalculateAllCharges(loan); loanChargeService.recalculateAllCharges(loan);
@@ -93,6 +93,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanSchedu
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService; import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
@@ -137,6 +138,7 @@ public class LoanAssemblerImpl implements LoanAssembler {
private final LoanDisbursementService loanDisbursementService; private final LoanDisbursementService loanDisbursementService;
private final LoanChargeService loanChargeService; private final LoanChargeService loanChargeService;
private final LoanOfficerService loanOfficerService; private final LoanOfficerService loanOfficerService;
private final LoanScheduleComponent loanSchedule;
@Override @Override
public Loan assembleFrom(final Long accountId) { public Loan assembleFrom(final Long accountId) {
@@ -293,6 +295,8 @@ public class LoanAssemblerImpl implements LoanAssembler {
throw new IllegalStateException("No loan application exists for either a client or group (or both)."); throw new IllegalStateException("No loan application exists for either a client or group (or both).");
} }
loanSchedule.updateLoanSchedule(loanApplication, loanScheduleModel);
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication); copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
loanApplication.setHelpers(defaultLoanLifecycleStateMachine); loanApplication.setHelpers(defaultLoanLifecycleStateMachine);
// TODO: review // TODO: review
@@ -858,8 +862,8 @@ public class LoanAssemblerImpl implements LoanAssembler {
final JsonElement parsedQuery = this.fromApiJsonHelper.parse(command.json()); final JsonElement parsedQuery = this.fromApiJsonHelper.parse(command.json());
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromApiJsonHelper); final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromApiJsonHelper);
final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query, false); final LoanScheduleModel loanScheduleModel = this.calculationPlatformService.calculateLoanSchedule(query, false);
loan.updateLoanSchedule(loanSchedule); loanSchedule.updateLoanSchedule(loan, loanScheduleModel);
loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanAccrualsProcessingService.reprocessExistingAccruals(loan);
loanChargeService.recalculateAllCharges(loan); loanChargeService.recalculateAllCharges(loan);
} }
@@ -34,6 +34,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl; import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -118,8 +119,9 @@ public class LoanAccountAutoStarter {
@Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class) @Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator,
LoanRepositoryWrapper loanRepositoryWrapper, LoanRepositoryWrapper loanRepositoryWrapper,
@Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, ExternalIdFactory externalIdFactory) { @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, ExternalIdFactory externalIdFactory,
LoanScheduleComponent loanSchedule) {
return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService, return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService,
externalIdFactory); externalIdFactory, loanSchedule);
} }
} }
@@ -146,6 +146,7 @@ import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusi
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl; import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventServiceImpl;
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService; import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
import org.apache.fineract.portfolio.loanaccount.service.adjustment.LoanAdjustmentService; import org.apache.fineract.portfolio.loanaccount.service.adjustment.LoanAdjustmentService;
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService; import org.apache.fineract.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService; import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService;
@@ -240,14 +241,15 @@ public class LoanAccountConfiguration {
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService, LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper, LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper,
LoanCollateralManagementMapper loanCollateralManagementMapper, LoanAccrualsProcessingService loanAccrualsProcessingService, LoanCollateralManagementMapper loanCollateralManagementMapper, LoanAccrualsProcessingService loanAccrualsProcessingService,
LoanDisbursementService loanDisbursementService, LoanChargeService loanChargeService, LoanOfficerService loanOfficerService) { LoanDisbursementService loanDisbursementService, LoanChargeService loanChargeService, LoanOfficerService loanOfficerService,
LoanScheduleComponent loanSchedule) {
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository, return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler, fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository, loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository,
rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository, rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository,
accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService, accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService,
loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService, loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService,
loanDisbursementService, loanChargeService, loanOfficerService); loanDisbursementService, loanChargeService, loanOfficerService, loanSchedule);
} }
@Bean @Bean
@@ -461,8 +463,8 @@ public class LoanAccountConfiguration {
@ConditionalOnMissingBean(LoanScheduleService.class) @ConditionalOnMissingBean(LoanScheduleService.class)
public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService, public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService,
ReprocessLoanTransactionsService reprocessLoanTransactionsService, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
LoanTransactionProcessingService loanTransactionProcessingService) { LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule) {
return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService); return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService, loanSchedule);
} }
@Bean @Bean
@@ -5310,8 +5310,8 @@ public class ClientLoanIntegrationTest extends BaseLoanIntegrationTest {
ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID); ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID);
assertEquals(2, loanSchedule.size()); assertEquals(2, loanSchedule.size());
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
LocalDate targetDate = LocalDate.of(2022, 9, 7); LocalDate targetDate = LocalDate.of(2022, 9, 7);
@@ -5906,10 +5906,10 @@ public class ClientLoanIntegrationTest extends BaseLoanIntegrationTest {
ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID); ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID);
assertEquals(2, loanSchedule.size()); assertEquals(2, loanSchedule.size());
assertEquals(0, loanSchedule.get(1).get("feeChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue"));
assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
LocalDate targetDate = LocalDate.of(2022, 9, 7); LocalDate targetDate = LocalDate.of(2022, 9, 7);
@@ -173,10 +173,10 @@ public class LoanSpecificDueDateChargeAfterMaturityTest extends BaseLoanIntegrat
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
assertEquals(2, loanSchedule.size()); assertEquals(2, loanSchedule.size());
assertEquals(0, loanSchedule.get(1).get("feeChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue"));
assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
targetDate = LocalDate.of(2011, 4, 5); targetDate = LocalDate.of(2011, 4, 5);
@@ -306,10 +306,10 @@ public class LoanSpecificDueDateChargeAfterMaturityTest extends BaseLoanIntegrat
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
assertEquals(2, loanSchedule.size()); assertEquals(2, loanSchedule.size());
assertEquals(0, loanSchedule.get(1).get("feeChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue"));
assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue"));
assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding"));
assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod"));
assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod"));
@@ -844,7 +844,7 @@ public class SchedulerJobsTestResults extends IntegrationTest {
ArrayList<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, ArrayList<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
loanID); loanID);
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"), Assertions.assertEquals(0.0f, repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job"); "Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
} }
@@ -1085,7 +1085,7 @@ public class SchedulerJobsTestResults extends IntegrationTest {
this.schedulerJobHelper.executeAndAwaitJob(jobName); this.schedulerJobHelper.executeAndAwaitJob(jobName);
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
loanID); loanID);
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"), Assertions.assertEquals(0.0f, repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job"); "Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
LocalDate lastBusinessDateBeforeFastForward = LocalDate.of(2019, 4, 2); LocalDate lastBusinessDateBeforeFastForward = LocalDate.of(2019, 4, 2);
@@ -1153,7 +1153,7 @@ public class SchedulerJobsTestResults extends IntegrationTest {
this.schedulerJobHelper.executeAndAwaitJob(jobName); this.schedulerJobHelper.executeAndAwaitJob(jobName);
List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
loanID2); loanID2);
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"), Assertions.assertEquals(0.0f, repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job"); "Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 5, 3)); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 5, 3));