This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 52c41b147151ffd453afa2b071599c11a1dad4cf Author: Jose Alberto Hernandez <[email protected]> AuthorDate: Wed Mar 19 09:03:35 2025 -0500 FINERACT-2190: Avoid recreation of loan repayment schedule periods --- .../portfolio/loanaccount/domain/Loan.java | 51 ---------- .../domain/LoanRepaymentScheduleInstallment.java | 106 ++++++++++++++++----- .../loanaccount/service/LoanScheduleService.java | 18 ++-- .../service/schedule/LoanScheduleComponent.java | 91 ++++++++++++++++++ ...dvancedPaymentScheduleTransactionProcessor.java | 8 +- ...cedPaymentScheduleTransactionProcessorTest.java | 4 +- ...nRescheduleRequestWritePlatformServiceImpl.java | 10 +- .../loanaccount/service/LoanAssemblerImpl.java | 8 +- .../starter/LoanAccountAutoStarter.java | 6 +- .../starter/LoanAccountConfiguration.java | 10 +- .../ClientLoanIntegrationTest.java | 12 +-- ...LoanSpecificDueDateChargeAfterMaturityTest.java | 16 ++-- .../integrationtests/SchedulerJobsTestResults.java | 6 +- 13 files changed, 229 insertions(+), 117 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 3d6cec432c..460036c47d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -57,7 +57,6 @@ import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; 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.LoanScheduleGenerator; 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.CreditAllocationTransactionType; import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; @@ -610,8 +608,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> { this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate(); this.submittedOnDate = submittedOnDate != null ? submittedOnDate : DateUtils.getBusinessLocalDate(); - updateLoanSchedule(loanScheduleModel); - updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); // Copy interest recalculation settings if interest recalculation is enabled @@ -1005,53 +1001,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> { 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() { if (this.getLoanRepaymentScheduleInstallmentsSize() > 0) { this.expectedMaturityDate = determineExpectedMaturityDate(); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index e3c64e4b3a..80c299aacc 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -38,10 +38,12 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; 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.repaymentwithpostdatedchecks.domain.PostDatedChecks; @Getter +@Setter @Entity @Table(name = "m_loan_repayment_schedule") public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom<Long> @@ -60,67 +62,67 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa @Column(name = "duedate", nullable = false) private LocalDate dueDate; - @Column(name = "principal_amount", scale = 6, precision = 19, nullable = true) + @Column(name = "principal_amount", scale = 6, precision = 19) 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; - @Column(name = "principal_writtenoff_derived", scale = 6, precision = 19, nullable = true) + @Column(name = "principal_writtenoff_derived", scale = 6, precision = 19) private BigDecimal principalWrittenOff; - @Column(name = "interest_amount", scale = 6, precision = 19, nullable = true) + @Column(name = "interest_amount", scale = 6, precision = 19) 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; - @Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true) + @Column(name = "interest_waived_derived", scale = 6, precision = 19) 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; - @Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true) + @Column(name = "accrual_interest_derived", scale = 6, precision = 19) 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; - @Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true) + @Column(name = "fee_charges_amount", scale = 6, precision = 19) 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; - @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; - @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; - @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; - @Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true) + @Column(name = "penalty_charges_amount", scale = 6, precision = 19) 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; - @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; - @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; - @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; - @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; - @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; @Column(name = "completed_derived", nullable = false) @@ -139,17 +141,17 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa // approach // https://blog.thepete.net/blog/2023/12/05/expand/contract-making-a-breaking-change-without-a-big-bang/ @Setter - @Column(name = "credits_amount", scale = 6, precision = 19, nullable = true) + @Column(name = "credits_amount", scale = 6, precision = 19) private BigDecimal creditedPrincipal; @Setter - @Column(name = "credited_interest", scale = 6, precision = 19, nullable = true) + @Column(name = "credited_interest", scale = 6, precision = 19) private BigDecimal creditedInterest; - @Column(name = "credited_fee", scale = 6, precision = 19, nullable = true) + @Column(name = "credited_fee", scale = 6, precision = 19) private BigDecimal creditedFee; - @Column(name = "credited_penalty", scale = 6, precision = 19, nullable = true) + @Column(name = "credited_penalty", scale = 6, precision = 19) private BigDecimal creditedPenalty; @Column(name = "is_down_payment", nullable = false) @@ -1068,4 +1070,58 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa 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()); + } + } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java index 99c2e9763c..057b4b6b36 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanScheduleService.java @@ -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.domain.LoanScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; +import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent; @RequiredArgsConstructor public class LoanScheduleService { @@ -36,16 +37,17 @@ public class LoanScheduleService { private final LoanChargeService loanChargeService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final LoanTransactionProcessingService loadTransactionProcessingService; + private final LoanScheduleComponent loanSchedule; /** * Ability to regenerate the repayment schedule based on the loans current details/state. */ public void regenerateRepaymentSchedule(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) { - final LoanScheduleModel loanSchedule = loan.regenerateScheduleModel(scheduleGeneratorDTO); - if (loanSchedule == null) { + final LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO); + if (loanScheduleModel == null) { return; } - loan.updateLoanSchedule(loanSchedule); + loanSchedule.updateLoanSchedule(loan, loanScheduleModel); final Set<LoanCharge> charges = loan.getActiveCharges(); for (final LoanCharge loanCharge : charges) { if (!loanCharge.isWaived()) { @@ -80,15 +82,15 @@ public class LoanScheduleService { public void regenerateRepaymentScheduleWithInterestRecalculation(final Loan loan, final ScheduleGeneratorDTO generatorDTO) { final LocalDate lastTransactionDate = loan.getLastUserTransactionDate(); - final LoanScheduleDTO loanSchedule = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan); - if (loanSchedule == null) { + final LoanScheduleDTO loanScheduleDTO = loadTransactionProcessingService.getRecalculatedSchedule(generatorDTO, loan); + if (loanScheduleDTO == null) { return; } // Either the installments got recalculated or the model - if (loanSchedule.getInstallments() != null) { - loan.updateLoanSchedule(loanSchedule.getInstallments()); + if (loanScheduleDTO.getInstallments() != null) { + loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getInstallments()); } else { - loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel()); + loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel()); } loan.setInterestRecalculatedOn(DateUtils.getBusinessLocalDate()); final LocalDate lastRepaymentDate = loan.getLastRepaymentPeriodDueDate(true); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/schedule/LoanScheduleComponent.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/schedule/LoanScheduleComponent.java new file mode 100644 index 0000000000..48029d58a2 --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/schedule/LoanScheduleComponent.java @@ -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); + } + +} diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index cdba9c9f41..5133ae504d 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -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.loanschedule.domain.LoanScheduleProcessingType; 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.data.PeriodDueDetails; import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; @@ -114,13 +115,15 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep private final EMICalculator emiCalculator; private final LoanRepositoryWrapper loanRepositoryWrapper; private final InterestRefundService interestRefundService; + private final LoanScheduleComponent loanSchedule; public AdvancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper, - InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory) { + InterestRefundService interestRefundService, ExternalIdFactory externalIdFactory, LoanScheduleComponent loanSchedule) { super(externalIdFactory); this.emiCalculator = emiCalculator; this.loanRepositoryWrapper = loanRepositoryWrapper; this.interestRefundService = interestRefundService; + this.loanSchedule = loanSchedule; } @Override @@ -1460,8 +1463,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep } } - loan.updateLoanSchedule(installmentsUpToTransactionDate); - loan.updateLoanScheduleDependentDerivedFields(); + loanSchedule.updateLoanSchedule(loan, installmentsUpToTransactionDate); if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && loan.isInterestRecalculationEnabled()) { updateRepaymentPeriodsAfterChargeOff(progressiveTransactionCtx, transactionDate, transactionsToBeReprocessed); diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java index f6552d7c16..ea8a7f4f7f 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java @@ -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.TransactionCtx; 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.data.ProgressiveLoanInterestScheduleModel; import org.apache.fineract.portfolio.loanproduct.domain.AllocationType; @@ -98,6 +99,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest { private AdvancedPaymentScheduleTransactionProcessor underTest; private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class); private static final LoanRepositoryWrapper loanRepositoryWrapper = Mockito.mock(LoanRepositoryWrapper.class); + private static final LoanScheduleComponent loanSchedule = Mockito.mock(LoanScheduleComponent.class); @BeforeAll public static void init() { @@ -112,7 +114,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest { @BeforeEach 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.setActionContext(ActionContext.DEFAULT); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index 815aaf991d..4f61df5e9f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -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.LoanUtilService; 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.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -115,6 +116,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche private final LoanAccrualsProcessingService loanAccrualsProcessingService; private final LoanChargeService loanChargeService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; + private final LoanScheduleComponent loanSchedule; /** * 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()); final LoanLifecycleStateMachine loanLifecycleStateMachine = null; loan.setHelpers(loanLifecycleStateMachine); - final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan, + final LoanScheduleDTO loanScheduleDTO = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan, loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate); // Either the installments got recalculated or the model - if (loanSchedule.getInstallments() != null) { - loan.updateLoanSchedule(loanSchedule.getInstallments()); + if (loanScheduleDTO.getInstallments() != null) { + loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getInstallments()); } else { - loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel()); + loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel()); } loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanChargeService.recalculateAllCharges(loan); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java index d878159434..6d8ff28723 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java @@ -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.mapper.LoanChargeMapper; 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.domain.LoanProduct; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; @@ -137,6 +138,7 @@ public class LoanAssemblerImpl implements LoanAssembler { private final LoanDisbursementService loanDisbursementService; private final LoanChargeService loanChargeService; private final LoanOfficerService loanOfficerService; + private final LoanScheduleComponent loanSchedule; @Override 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)."); } + loanSchedule.updateLoanSchedule(loanApplication, loanScheduleModel); + copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication); loanApplication.setHelpers(defaultLoanLifecycleStateMachine); // TODO: review @@ -858,8 +862,8 @@ public class LoanAssemblerImpl implements LoanAssembler { final JsonElement parsedQuery = this.fromApiJsonHelper.parse(command.json()); final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromApiJsonHelper); - final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query, false); - loan.updateLoanSchedule(loanSchedule); + final LoanScheduleModel loanScheduleModel = this.calculationPlatformService.calculateLoanSchedule(query, false); + loanSchedule.updateLoanSchedule(loan, loanScheduleModel); loanAccrualsProcessingService.reprocessExistingAccruals(loan); loanChargeService.recalculateAllCharges(loan); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java index 4a88a47def..2901e07160 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java @@ -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.RBILoanRepaymentScheduleTransactionProcessor; 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.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -118,8 +119,9 @@ public class LoanAccountAutoStarter { @Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class) public AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator, LoanRepositoryWrapper loanRepositoryWrapper, - @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, ExternalIdFactory externalIdFactory) { + @Lazy ProgressiveLoanInterestRefundServiceImpl progressiveLoanInterestRefundService, ExternalIdFactory externalIdFactory, + LoanScheduleComponent loanSchedule) { return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, loanRepositoryWrapper, progressiveLoanInterestRefundService, - externalIdFactory); + externalIdFactory, loanSchedule); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java index 607dda3a76..cf957cd363 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java @@ -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.ReprocessLoanTransactionsService; 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.service.LoanDropdownReadPlatformService; import org.apache.fineract.portfolio.loanproduct.service.LoanProductReadPlatformService; @@ -240,14 +241,15 @@ public class LoanAccountConfiguration { LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService, LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler, LoanChargeMapper loanChargeMapper, 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, fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler, loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository, rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository, accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService, loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService, - loanDisbursementService, loanChargeService, loanOfficerService); + loanDisbursementService, loanChargeService, loanOfficerService, loanSchedule); } @Bean @@ -461,8 +463,8 @@ public class LoanAccountConfiguration { @ConditionalOnMissingBean(LoanScheduleService.class) public LoanScheduleService loanScheduleService(LoanChargeService loanChargeService, ReprocessLoanTransactionsService reprocessLoanTransactionsService, - LoanTransactionProcessingService loanTransactionProcessingService) { - return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService); + LoanTransactionProcessingService loanTransactionProcessingService, LoanScheduleComponent loanSchedule) { + return new LoanScheduleService(loanChargeService, reprocessLoanTransactionsService, loanTransactionProcessingService, loanSchedule); } @Bean diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java index dd26bfcfad..d58bf9f0da 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java @@ -5310,8 +5310,8 @@ public class ClientLoanIntegrationTest extends BaseLoanIntegrationTest { ArrayList<HashMap> loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID); assertEquals(2, loanSchedule.size()); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); 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); assertEquals(2, loanSchedule.size()); - assertEquals(0, loanSchedule.get(1).get("feeChargesDue")); - assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(1000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(1000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); LocalDate targetDate = LocalDate.of(2022, 9, 7); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java index bb7b14e3a4..6b8b286cc2 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanSpecificDueDateChargeAfterMaturityTest.java @@ -173,10 +173,10 @@ public class LoanSpecificDueDateChargeAfterMaturityTest extends BaseLoanIntegrat ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); assertEquals(2, loanSchedule.size()); - assertEquals(0, loanSchedule.get(1).get("feeChargesDue")); - assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); targetDate = LocalDate.of(2011, 4, 5); @@ -306,10 +306,10 @@ public class LoanSpecificDueDateChargeAfterMaturityTest extends BaseLoanIntegrat ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID); assertEquals(2, loanSchedule.size()); - assertEquals(0, loanSchedule.get(1).get("feeChargesDue")); - assertEquals(0, loanSchedule.get(1).get("feeChargesOutstanding")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesDue")); - assertEquals(0, loanSchedule.get(1).get("penaltyChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("feeChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("feeChargesOutstanding")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesDue")); + assertEquals(0.0f, loanSchedule.get(1).get("penaltyChargesOutstanding")); assertEquals(10000.0f, loanSchedule.get(1).get("totalDueForPeriod")); assertEquals(10000.0f, loanSchedule.get(1).get("totalOutstandingForPeriod")); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java index d39e8012eb..3045df0243 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java @@ -844,7 +844,7 @@ public class SchedulerJobsTestResults extends IntegrationTest { ArrayList<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, 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"); } @@ -1085,7 +1085,7 @@ public class SchedulerJobsTestResults extends IntegrationTest { this.schedulerJobHelper.executeAndAwaitJob(jobName); List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, 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"); LocalDate lastBusinessDateBeforeFastForward = LocalDate.of(2019, 4, 2); @@ -1153,7 +1153,7 @@ public class SchedulerJobsTestResults extends IntegrationTest { this.schedulerJobHelper.executeAndAwaitJob(jobName); List<HashMap> repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, 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"); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 5, 3));
