You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2024/07/12 09:43:54 UTC
(fineract) branch develop updated: FINERACT-2090: restructure loan disbursements
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
The following commit(s) were added to refs/heads/develop by this push:
new c9d1b81cc FINERACT-2090: restructure loan disbursements
c9d1b81cc is described below
commit c9d1b81cc695ba7d20682462457e9b7dcd2f497e
Author: Kristof Jozsa <kr...@gmail.com>
AuthorDate: Wed Jun 26 14:34:54 2024 +0200
FINERACT-2090: restructure loan disbursements
more refactors
---
.../loan/LoanAccountOwnerTransferBusinessStep.java | 14 +-
.../investor/service/AccountingServiceImpl.java | 8 +-
.../LoanAccountOwnerTransferServiceImpl.java | 12 +-
.../LoanAccountOwnerTransferBusinessStepTest.java | 10 +-
.../LoanAccountOwnerTransferServiceTest.java | 2 +-
.../domain/DefaultLoanLifecycleStateMachine.java | 2 +-
.../portfolio/loanaccount/domain/Loan.java | 175 ++--------------
.../loanaccount/domain/LoanTransaction.java | 36 ++--
.../AbstractCumulativeLoanScheduleGenerator.java | 2 +-
.../service/LoanArrearsAgingServiceImpl.java | 2 +-
.../loan/CheckLoanRepaymentDueBusinessStep.java | 2 +-
.../CheckLoanRepaymentOverdueBusinessStep.java | 3 +-
...LoanScheduleCalculationPlatformServiceImpl.java | 4 +-
.../serialization/LoanTransactionValidator.java | 171 +++++++++++----
.../LoanWritePlatformServiceJpaRepositoryImpl.java | 233 ++++++++++++---------
.../starter/LoanAccountConfiguration.java | 27 +--
.../CheckLoanRepaymentDueBusinessStepTest.java | 8 +-
.../CheckLoanRepaymentOverdueBusinessStepTest.java | 24 +--
.../LoanRepaymentBusinessEventSerializerTest.java | 4 +-
.../DefaultLoanLifecycleStateMachineTest.java | 4 +-
.../LoanRepaymentRescheduleAtDisbursementTest.java | 21 +-
21 files changed, 379 insertions(+), 385 deletions(-)
diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java
index 99b9c8ef8..f58aff581 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java
@@ -158,15 +158,13 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails();
details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer);
- details.setTotalOutstanding(Objects.requireNonNullElse(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO));
- details.setTotalPrincipalOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
- details.setTotalInterestOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
+ details.setTotalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalOutstanding(), BigDecimal.ZERO));
+ details.setTotalPrincipalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
+ details.setTotalInterestOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
details.setTotalFeeChargesOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
+ Objects.requireNonNullElse(loan.getSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
details.setTotalPenaltyChargesOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
+ Objects.requireNonNullElse(loan.getSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO));
return details;
}
@@ -179,7 +177,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
}
private boolean isTransferable(final Loan loan) {
- return MathUtil.nullToDefault(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO).compareTo(BigDecimal.ZERO) > 0;
+ return MathUtil.nullToDefault(loan.getSummary().getTotalOutstanding(), BigDecimal.ZERO).compareTo(BigDecimal.ZERO) > 0;
}
private void handleSameDaySaleAndBuyback(final LocalDate settlementDate, final List<ExternalAssetOwnerTransfer> transferDataList,
diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/AccountingServiceImpl.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/AccountingServiceImpl.java
index 09416e5b0..394c53111 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/AccountingServiceImpl.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/AccountingServiceImpl.java
@@ -80,10 +80,10 @@ public class AccountingServiceImpl implements AccountingService {
// transaction properties
final Long transactionId = transfer.getId();
final LocalDate transactionDate = transfer.getSettlementDate();
- final BigDecimal principalAmount = loan.getLoanSummary().getTotalPrincipalOutstanding();
- final BigDecimal interestAmount = loan.getLoanSummary().getTotalInterestOutstanding();
- final BigDecimal feesAmount = loan.getLoanSummary().getTotalFeeChargesOutstanding();
- final BigDecimal penaltiesAmount = loan.getLoanSummary().getTotalPenaltyChargesOutstanding();
+ final BigDecimal principalAmount = loan.getSummary().getTotalPrincipalOutstanding();
+ final BigDecimal interestAmount = loan.getSummary().getTotalInterestOutstanding();
+ final BigDecimal feesAmount = loan.getSummary().getTotalFeeChargesOutstanding();
+ final BigDecimal penaltiesAmount = loan.getSummary().getTotalPenaltyChargesOutstanding();
final BigDecimal overPaymentAmount = loan.getTotalOverpaid();
// Moving money to asset transfer account
diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java
index df211085f..9d5517cc7 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java
@@ -165,15 +165,13 @@ public class LoanAccountOwnerTransferServiceImpl implements LoanAccountOwnerTran
ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails();
details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer);
- details.setTotalOutstanding(Objects.requireNonNullElse(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO));
- details.setTotalPrincipalOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
- details.setTotalInterestOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
+ details.setTotalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalOutstanding(), BigDecimal.ZERO));
+ details.setTotalPrincipalOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO));
+ details.setTotalInterestOutstanding(Objects.requireNonNullElse(loan.getSummary().getTotalInterestOutstanding(), BigDecimal.ZERO));
details.setTotalFeeChargesOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
+ Objects.requireNonNullElse(loan.getSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO));
details.setTotalPenaltyChargesOutstanding(
- Objects.requireNonNullElse(loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
+ Objects.requireNonNullElse(loan.getSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO));
details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO));
return details;
}
diff --git a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java
index 02643749b..81500a08f 100644
--- a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java
+++ b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java
@@ -203,7 +203,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
final Loan loanForProcessing = Mockito.mock(Loan.class);
when(loanForProcessing.getId()).thenReturn(1L);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
ExternalAssetOwnerTransfer secondResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
when(firstResponseItem.getStatus()).thenReturn(ExternalTransferStatus.BUYBACK);
@@ -248,7 +248,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ONE);
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.ACTIVE);
// when
@@ -296,7 +296,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ZERO);
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.DECLINED);
@@ -340,7 +340,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
ExternalAssetOwnerTransfer newTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
when(externalAssetOwnerTransferRepository.save(any())).thenReturn(firstResponseItem).thenReturn(newTransfer);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ONE.negate());
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE.negate());
when(newTransfer.getStatus()).thenReturn(ExternalTransferStatus.DECLINED);
@@ -389,7 +389,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
final Loan loanForProcessing = Mockito.mock(Loan.class);
when(loanForProcessing.getId()).thenReturn(1L);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE);
ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
diff --git a/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java b/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java
index adf33d889..74b1991a2 100644
--- a/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java
+++ b/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java
@@ -133,7 +133,7 @@ public class LoanAccountOwnerTransferServiceTest {
final Loan loanForProcessing = Mockito.mock(Loan.class);
when(loanForProcessing.getId()).thenReturn(1L);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
ExternalAssetOwnerTransfer pendingBuybackTransfer = Mockito.mock(ExternalAssetOwnerTransfer.class);
when(pendingBuybackTransfer.getStatus()).thenReturn(BUYBACK);
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
index 26eb2980f..52b84de35 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java
@@ -85,7 +85,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
if (anyOfAllowedWhenComingFrom(from, LoanStatus.APPROVED, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
newState = activeTransition();
} else if (from.isOverpaid() && loan.getTotalOverpaidAsMoney().isZero()) {
- if (loan.getLoanSummary().getTotalOutstanding(loan.getCurrency()).isZero()) {
+ if (loan.getSummary().getTotalOutstanding(loan.getCurrency()).isZero()) {
newState = closeObligationsMetTransition();
} else {
newState = activeTransition();
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 4ef74ff9c..448946b55 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
@@ -613,7 +613,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return this.loanRepaymentScheduleDetail.getNumberOfRepayments();
}
- private LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) {
+ public LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) {
if (this.summary == null) {
this.summary = LoanSummary.create(feeChargesDueAtDisbursement);
} else {
@@ -634,7 +634,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
this.summary.updateTotalWaived(this.summary.getTotalWaived().subtract(amountWaived));
}
- private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
+ public BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
return getActiveCharges().stream() //
.filter(LoanCharge::isDueAtDisbursement) //
.map(LoanCharge::amount) //
@@ -1721,108 +1721,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
.collect(Collectors.toList());
}
- public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
- final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) {
- final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
-
- this.disbursedBy = currentUser;
- updateLoanScheduleDependentDerivedFields();
-
- actualChanges.put(LOCALE, command.locale());
- actualChanges.put(DATE_FORMAT, command.dateFormat());
- actualChanges.put(ACTUAL_DISBURSEMENT_DATE, command.stringValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE));
-
- HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
-
- // validate if disbursement date is a holiday or a non-working day
- validateDisbursementDateIsOnNonWorkingDay(holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
- validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
-
- regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(this.repaymentScheduleDetail().isInterestRecalculationEnabled(),
- isDisbursementMissed(), scheduleGeneratorDTO);
-
- updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
- updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
- handleDisbursementTransaction(actualDisbursementDate, paymentDetail);
- updateLoanSummaryDerivedFields();
- final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
-
- /*
- * Add an interest applied transaction of the interest is accrued upfront (Up front accrual), no accounting or
- * cash based accounting is selected
- */
- if (((isMultiDisburmentLoan() && getDisbursedLoanDisbursementDetails().size() == 1) || !isMultiDisburmentLoan())
- && isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
- ExternalId externalId = ExternalId.empty();
- if (TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
- externalId = ExternalId.generate();
- }
- final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
- actualDisbursementDate, externalId);
- addLoanTransaction(interestAppliedTransaction);
- }
-
- ChangedTransactionDetail result = reprocessTransactionForDisbursement();
- this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, this);
- actualChanges.put(PARAM_STATUS, LoanEnumerations.status(this.loanStatus));
- return result;
- }
-
- private void regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(boolean interestRecalculationEnabledParam,
- boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO) {
- LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
- if ((interestRecalculationEnabledParam && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
- regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
- }
- }
-
- private List<LoanDisbursementDetails> getDisbursedLoanDisbursementDetails() {
+ public List<LoanDisbursementDetails> getDisbursedLoanDisbursementDetails() {
return getDisbursementDetails().stream() //
.filter(it -> it.actualDisbursementDate() != null) //
.collect(Collectors.toList());
}
- public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
- final LocalDate actualDisbursementDate, BigDecimal emiAmount, LocalDate nextPossibleRepaymentDate,
- LocalDate rescheduledRepaymentDate) {
- boolean isEmiAmountChanged = false;
- if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.isCanDefineInstallmentAmount()) && emiAmount != null
- && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
- if (this.loanProduct.isMultiDisburseLoan()) {
- final LocalDate dateValue = null;
- final boolean isSpecificToInstallment = false;
- final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = scheduleGeneratorDTO
- .isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
- LocalDate effectiveDateFrom = actualDisbursementDate;
- if (!isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled && actualDisbursementDate.equals(nextPossibleRepaymentDate)) {
- effectiveDateFrom = nextPossibleRepaymentDate.plusDays(1);
- }
- LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
- effectiveDateFrom, emiAmount, dateValue, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue());
- this.loanTermVariations.add(loanVariationTerms);
- } else {
- this.fixedEmiAmount = emiAmount;
- }
- isEmiAmountChanged = true;
- }
- if (rescheduledRepaymentDate != null && this.loanProduct.isMultiDisburseLoan()) {
- final boolean isSpecificToInstallment = false;
- LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(),
- nextPossibleRepaymentDate, emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, this,
- LoanStatus.ACTIVE.getValue());
- this.loanTermVariations.add(loanVariationTerms);
- }
-
- if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
- || rescheduledRepaymentDate != null) {
- if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
- regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
- } else {
- regenerateRepaymentSchedule(scheduleGeneratorDTO);
- }
- }
- }
-
public boolean canDisburse(final LocalDate actualDisbursementDate) {
LocalDate loanSubmittedOnDate = this.submittedOnDate;
final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSED, this);
@@ -1918,25 +1822,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
}
- private ChangedTransactionDetail reprocessTransactionForDisbursement() {
- ChangedTransactionDetail changedTransactionDetail = null;
- if (this.loanProduct.isMultiDisburseLoan()) {
- final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing();
- if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
- final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
- .determineProcessor(this.transactionProcessingStrategyCode);
- changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(getDisbursementDate(),
- allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(), getActiveCharges());
- for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
- mapEntry.getValue().updateLoan(this);
- }
- this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
- }
- updateLoanSummaryDerivedFields();
- }
- return changedTransactionDetail;
- }
-
private Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
LocalDate date = null;
@@ -1993,15 +1878,19 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
private boolean isDisbursementAllowed() {
List<LoanDisbursementDetails> disbursementDetails = getDisbursementDetails();
- return disbursementDetails == null || disbursementDetails.isEmpty()
+ boolean isSingleDisburseLoanDisbursementAllowed = disbursementDetails == null || disbursementDetails.isEmpty()
|| disbursementDetails.stream().anyMatch(it -> it.actualDisbursementDate() == null);
+ boolean isMultiDisburseLoanDisbursementAllowed = isMultiDisburmentLoan()
+ && (disbursementDetails == null || disbursementDetails.stream().filter(it -> it.actualDisbursementDate() != null)
+ .count() < loanProduct.getLoanProductTrancheDetails().maxTrancheCount());
+ return isSingleDisburseLoanDisbursementAllowed || isMultiDisburseLoanDisbursementAllowed;
}
private boolean atLeastOnceDisbursed() {
return getDisbursementDetails().stream().anyMatch(it -> it.actualDisbursementDate() != null);
}
- private void updateLoanRepaymentPeriodsDerivedFields(final LocalDate actualDisbursementDate) {
+ public void updateLoanRepaymentPeriodsDerivedFields(final LocalDate actualDisbursementDate) {
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
for (final LoanRepaymentScheduleInstallment repaymentPeriod : installments) {
repaymentPeriod.updateDerivedFields(loanCurrency(), actualDisbursementDate);
@@ -2075,7 +1964,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return interestRate;
}
- private void handleDisbursementTransaction(final LocalDate disbursedOn, final PaymentDetail paymentDetail) {
+ public void handleDisbursementTransaction(final LocalDate disbursedOn, final PaymentDetail paymentDetail) {
// add repayment transaction to track incoming money from client to mfi
// for (charges due at time of disbursement)
@@ -2099,11 +1988,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
* create a Charge applied transaction if Up front Accrual, None or Cash based accounting is enabled
*/
if ((charge.getCharge().getChargeTimeType().equals(ChargeTimeType.DISBURSEMENT.getValue())
- && disbursedOn.equals(actualDisbursementDate) && (actualDisbursementDate != null) && !charge.isWaived()
- && !charge.isFullyPaid())
+ && disbursedOn.equals(actualDisbursementDate) && !charge.isWaived() && !charge.isFullyPaid())
|| (charge.getCharge().getChargeTimeType().equals(ChargeTimeType.TRANCHE_DISBURSEMENT.getValue())
- && disbursedOn.equals(actualDisbursementDate) && (actualDisbursementDate != null) && !charge.isWaived()
- && !charge.isFullyPaid())) {
+ && disbursedOn.equals(actualDisbursementDate) && !charge.isWaived() && !charge.isFullyPaid())) {
if (totalFeeChargesDueAtDisbursement.isGreaterThanZero() && !charge.getChargePaymentMode().isPaymentModeAccountTransfer()) {
charge.markAsFullyPaid();
// Add "Loan Charge Paid By" details to this transaction
@@ -2244,7 +2131,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(this.loanStatus));
final LocalDate actualDisbursementDate = getDisbursementDate();
- final boolean isScheduleRegenerateRequired = isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate);
+ final boolean isScheduleRegenerateRequired = isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate);
this.actualDisbursementDate = null;
this.disbursedBy = null;
boolean isDisbursedAmountChanged = !MathUtil.isEqualTo(approvedPrincipal,
@@ -2461,7 +2348,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
if (loanTransaction.isRecoveryRepayment()
- && loanTransaction.getAmount(loanCurrency()).getAmount().compareTo(getLoanSummary().getTotalWrittenOff()) > 0) {
+ && loanTransaction.getAmount(loanCurrency()).getAmount().compareTo(getSummary().getTotalWrittenOff()) > 0) {
final String errorMessage = "The transaction amount cannot greater than the remaining written off amount.";
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage);
}
@@ -3210,8 +3097,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public boolean isAllTranchesNotDisbursed() {
LoanStatus actualLoanStatus = LoanStatus.fromInt(this.loanStatus);
- return this.loanProduct.isMultiDisburseLoan() && (actualLoanStatus.isActive() || actualLoanStatus.isApproved()
- || actualLoanStatus.isClosedObligationsMet() || actualLoanStatus.isOverpaid()) && isDisbursementAllowed();
+ boolean isInRightStatus = actualLoanStatus.isActive() || actualLoanStatus.isApproved() || actualLoanStatus.isClosedObligationsMet()
+ || actualLoanStatus.isOverpaid();
+ return this.loanProduct.isMultiDisburseLoan() && isInRightStatus && isDisbursementAllowed();
}
private boolean hasDisbursementTransaction() {
@@ -3263,7 +3151,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return principal;
}
- private boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
+ public boolean isActualDisbursedOnDateEarlierOrLaterThanExpected(final LocalDate actualDisbursedOnDate) {
boolean isRegenerationRequired = false;
if (this.loanProduct.isMultiDisburseLoan()) {
LoanDisbursementDetails details = fetchLastDisburseDetail();
@@ -3274,10 +3162,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return isRegenerationRequired || !DateUtils.isEqual(actualDisbursedOnDate, this.expectedDisbursementDate);
}
- private boolean isRepaymentScheduleRegenerationRequiredForDisbursement(final LocalDate actualDisbursementDate) {
- return isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate);
- }
-
private Money getTotalPaidInRepayments() {
Money cumulativePaid = Money.zero(loanCurrency());
@@ -3814,20 +3698,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return dueRepaymentPeriodDate.minusDays(1);// get 2n-1 range date from startDate
}
- private void validateDisbursementDateIsOnNonWorkingDay(final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
- if (!allowTransactionsOnNonWorkingDay && !WorkingDaysUtil.isWorkingDay(workingDays, getDisbursementDate())) {
- final String errorMessage = "Expected disbursement date cannot be on a non working day";
- throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage, getExpectedDisbursedOnLocalDate());
- }
- }
-
- private void validateDisbursementDateIsOnHoliday(final boolean allowTransactionsOnHoliday, final List<Holiday> holidays) {
- if (!allowTransactionsOnHoliday && HolidayUtil.isHoliday(getDisbursementDate(), holidays)) {
- final String errorMessage = "Expected disbursement date cannot be on a holiday";
- throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage, getExpectedDisbursedOnLocalDate());
- }
- }
-
public void validateRepaymentDateIsOnNonWorkingDay(final LocalDate repaymentDate, final WorkingDays workingDays,
final boolean allowTransactionsOnNonWorkingDay) {
if (!allowTransactionsOnNonWorkingDay && !WorkingDaysUtil.isWorkingDay(workingDays, repaymentDate)) {
@@ -4838,13 +4708,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
}
- /**
- * @return Loan summary embedded object
- **/
- public LoanSummary getLoanSummary() {
- return this.summary;
- }
-
public void updateRescheduledByUser(AppUser rescheduledByUser) {
this.rescheduledByUser = rescheduledByUser;
}
@@ -5245,7 +5108,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) {
Money[] receivables = retriveIncomeOutstandingTillDate(closureDate);
- Money totalPrincipal = Money.of(getCurrency(), this.getLoanSummary().getTotalPrincipalOutstanding());
+ Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
totalPrincipal = totalPrincipal.minus(receivables[3]);
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index b2f20684a..eb7548fd7 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -262,25 +262,25 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate,
- loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(),
- loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(),
- loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
+ loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
+ loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
+ loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
}
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate,
- loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(),
- loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(),
- loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
+ loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
+ loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
+ loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
}
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate,
- loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(),
- loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(),
- loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
+ loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
+ loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
+ loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
}
public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail,
@@ -369,19 +369,19 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
}
public static LoanTransaction chargeOff(final Loan loan, final LocalDate chargeOffDate, final ExternalId externalId) {
- BigDecimal principalPortion = loan.getLoanSummary().getTotalPrincipalOutstanding().compareTo(BigDecimal.ZERO) != 0
- ? loan.getLoanSummary().getTotalPrincipalOutstanding()
+ BigDecimal principalPortion = loan.getSummary().getTotalPrincipalOutstanding().compareTo(BigDecimal.ZERO) != 0
+ ? loan.getSummary().getTotalPrincipalOutstanding()
: null;
- BigDecimal interestPortion = loan.getLoanSummary().getTotalInterestOutstanding().compareTo(BigDecimal.ZERO) != 0
- ? loan.getLoanSummary().getTotalInterestOutstanding()
+ BigDecimal interestPortion = loan.getSummary().getTotalInterestOutstanding().compareTo(BigDecimal.ZERO) != 0
+ ? loan.getSummary().getTotalInterestOutstanding()
: null;
- BigDecimal feePortion = loan.getLoanSummary().getTotalFeeChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
- ? loan.getLoanSummary().getTotalFeeChargesOutstanding()
+ BigDecimal feePortion = loan.getSummary().getTotalFeeChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
+ ? loan.getSummary().getTotalFeeChargesOutstanding()
: null;
- BigDecimal penaltyPortion = loan.getLoanSummary().getTotalPenaltyChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
- ? loan.getLoanSummary().getTotalPenaltyChargesOutstanding()
+ BigDecimal penaltyPortion = loan.getSummary().getTotalPenaltyChargesOutstanding().compareTo(BigDecimal.ZERO) != 0
+ ? loan.getSummary().getTotalPenaltyChargesOutstanding()
: null;
- BigDecimal totalOutstanding = loan.getLoanSummary().getTotalOutstanding();
+ BigDecimal totalOutstanding = loan.getSummary().getTotalOutstanding();
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_OFF.getValue(), chargeOffDate, totalOutstanding,
principalPortion, interestPortion, feePortion, penaltyPortion, null, false, null, externalId);
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
index fbe32e9b9..b68e5d5f9 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java
@@ -2494,7 +2494,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
currency, applyInterestRecalculation);
retainedInstallments.addAll(newRepaymentScheduleInstallments);
loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
- loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getLoanSummary().getTotalInterestCharged()));
+ loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getSummary().getTotalInterestCharged()));
} else {
loanApplicationTerms.getLoanTermVariations().resetVariations();
periods.clear();
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java
index 795bbec22..d4169fb80 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanArrearsAgingServiceImpl.java
@@ -105,7 +105,7 @@ public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService {
Map<Long, List<LoanSchedulePeriodData>> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema,
originalScheduleExtractor);
if (scheduleDate.size() > 0) {
- List<Map<String, Object>> transactions = getLoanSummary(loan.getId(), loan.getLoanSummary());
+ List<Map<String, Object>> transactions = getLoanSummary(loan.getId(), loan.getSummary());
updateScheduleWithPaidDetail(scheduleDate, transactions);
createInsertStatements(updateStatement, scheduleDate, count == 0);
if (updateStatement.size() == 1) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java
index fc815f937..8f6d45e3c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStep.java
@@ -80,7 +80,7 @@ public class CheckLoanRepaymentDueBusinessStep implements LoanCOBBusinessStep {
LoanRepaymentScheduleInstallment repaymentScheduleInstallment, LocalDate repaymentDate, List<LoanStatus> nonDisbursedStatuses) {
return repaymentDate.minusDays(numberOfDaysBeforeDueDateToRaiseEvent).equals(currentDate)
&& !nonDisbursedStatuses.contains(loan.getStatus())
- && loan.getLoanSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0
+ && loan.getSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0
&& repaymentScheduleInstallment.getTotalOutstanding(loan.getCurrency()).isGreaterThanZero();
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java
index 9d51055d1..d3714a381 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStep.java
@@ -45,8 +45,7 @@ public class CheckLoanRepaymentOverdueBusinessStep implements LoanCOBBusinessSte
public Loan execute(Loan loan) {
List<LoanStatus> nonDisbursedStatuses = Arrays.asList(LoanStatus.INVALID, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL,
LoanStatus.APPROVED);
- if (!nonDisbursedStatuses.contains(loan.getStatus())
- && loan.getLoanSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0) {
+ if (!nonDisbursedStatuses.contains(loan.getStatus()) && loan.getSummary().getTotalOutstanding().compareTo(BigDecimal.ZERO) > 0) {
log.debug("start processing loan repayment overdue business step for loan with Id [{}]", loan.getId());
Long numberOfDaysAfterDueDateToRaiseEvent = configurationDomainService.retrieveRepaymentOverdueDays();
if (loan.getLoanProduct().getOverDueDaysForRepaymentEvent() != null) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
index dce06fdf9..0f4dd818f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
@@ -97,8 +97,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
if (loan.loanProduct().isMultiDisburseLoan()) {
BigDecimal disbursedAmount = loan.getDisbursedAmount();
- BigDecimal principalRepaid = loan.getLoanSummary().getTotalPrincipalRepaid();
- BigDecimal principalWrittenOff = loan.getLoanSummary().getTotalPrincipalWrittenOff();
+ BigDecimal principalRepaid = loan.getSummary().getTotalPrincipalRepaid();
+ BigDecimal principalWrittenOff = loan.getSummary().getTotalPrincipalWrittenOff();
if (disbursedAmount.subtract(principalWrittenOff).subtract(principalRepaid).compareTo(BigDecimal.ZERO) <= 0) {
return;
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidator.java
index 4ff6935bc..feb506855 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidator.java
@@ -38,11 +38,15 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
+import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
+import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
@@ -52,26 +56,35 @@ import org.apache.fineract.organisation.monetary.exception.CurrencyNotFoundExcep
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
+import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
+import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
import org.apache.fineract.portfolio.calendar.exception.NotValidRecurringDateException;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
+import org.apache.fineract.portfolio.collateralmanagement.exception.LoanCollateralAmountNotSufficientException;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
+import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanChargeRefundException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanRepaymentScheduleNotFoundException;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
+import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
@Component
@@ -81,8 +94,11 @@ public final class LoanTransactionValidator {
private final FromJsonHelper fromApiJsonHelper;
private final LoanApplicationValidator fromApiJsonDeserializer;
private final LoanRepository loanRepository;
+ private final LoanRepositoryWrapper loanRepositoryWrapper;
private final ApplicationCurrencyRepository applicationCurrencyRepository;
private final LoanUtilService loanUtilService;
+ private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
+ private final CalendarInstanceRepository calendarInstanceRepository;
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
@@ -91,13 +107,129 @@ public final class LoanTransactionValidator {
}
}
- public void validateDisbursement(final String json, boolean isAccountTransfer) {
-
+ public void validateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
+ String json = command.json();
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
- Set<String> disbursementParameters = null;
+ final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+ this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, getDisbursementParameters(isAccountTransfer));
+
+ LoanApplicationValidator.validateOrThrow("loan.disbursement", baseDataValidator -> {
+ final JsonElement element = this.fromApiJsonHelper.parse(json);
+ final LocalDate actualDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("actualDisbursementDate", element);
+ baseDataValidator.reset().parameter("actualDisbursementDate").value(actualDisbursementDate).notNull();
+
+ final String note = this.fromApiJsonHelper.extractStringNamed("note", element);
+ baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
+
+ final BigDecimal principal = this.fromApiJsonHelper
+ .extractBigDecimalWithLocaleNamed(LoanApiConstants.principalDisbursedParameterName, element);
+ baseDataValidator.reset().parameter(LoanApiConstants.principalDisbursedParameterName).value(principal).ignoreIfNull()
+ .positiveAmount();
+
+ final BigDecimal netDisbursalAmount = this.fromApiJsonHelper
+ .extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName, element);
+ baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
+ .ignoreIfNull().positiveAmount();
+
+ final BigDecimal emiAmount = this.fromApiJsonHelper
+ .extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName, element);
+ baseDataValidator.reset().parameter(LoanApiConstants.fixedEmiAmountParameterName).value(emiAmount).ignoreIfNull()
+ .positiveAmount().notGreaterThanMax(principal);
+
+ validatePaymentDetails(baseDataValidator, element);
+
+ if (command.parameterExists("postDatedChecks")) {
+ this.validateDisbursementWithPostDatedChecks(command.json(), loanId);
+ }
+
+ final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
+ validateLoanClientIsActive(loan);
+ validateLoanGroupIsActive(loan);
+
+ if (loan.isChargedOff() && DateUtils.isBefore(actualDisbursementDate, loan.getChargedOffOnDate())) {
+ throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ + loan.getId()
+ + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
+ loan.getId());
+ }
+
+ boolean isSingleDisburseLoan = !loan.getLoanProduct().isMultiDisburseLoan();
+ boolean isSingleDisburseNotApprovedOrDisbursedAlready = isSingleDisburseLoan && !(loan.isApproved() && loan.isNotDisbursed());
+ boolean isMultiDisburseLoanAndAllTranchesDisbursed = loan.getLoanProduct().isMultiDisburseLoan()
+ && !loan.isAllTranchesNotDisbursed();
+ if (isSingleDisburseNotApprovedOrDisbursedAlready || isMultiDisburseLoanAndAllTranchesDisbursed) {
+ final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state.";
+ final ApiParameterError error = ApiParameterError
+ .generalError("error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage);
+ baseDataValidator.getDataValidationErrors().add(error);
+ }
+
+ final BigDecimal disbursedAmount = loan.getDisbursedAmount();
+ final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
+
+ if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
+ BigDecimal totalCollateral = collectTotalCollateral(loanCollateralManagements);
+
+ // Validate the loan collateral value against the disbursedAmount
+ if (disbursedAmount.compareTo(totalCollateral) > 0) {
+ throw new LoanCollateralAmountNotSufficientException(disbursedAmount);
+ }
+ }
+
+ // validate ActualDisbursement Date Against Expected Disbursement Date
+ LoanProduct loanProduct = loan.loanProduct();
+ if (loanProduct.isSyncExpectedWithDisbursementDate()) {
+ if (!loan.getExpectedDisbursedOnLocalDate().equals(actualDisbursementDate)) {
+ throw new DateMismatchException(actualDisbursementDate, loan.getExpectedDisbursedOnLocalDate());
+ }
+ }
+
+ entityDatatableChecksWritePlatformService.runTheCheckForProduct(loan.getId(), EntityTables.LOAN.getName(),
+ StatusEnum.DISBURSE.getValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
+
+ ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
+ final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
+ CalendarEntityType.LOANS.getValue());
+ if (loan.isSyncDisbursementWithMeeting()) {
+ validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance,
+ scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), scheduleGeneratorDTO.getNumberOfdays());
+ }
+
+ // validate if disbursement date is a holiday or a non-working day
+ HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
+ if (!holidayDetailDTO.isAllowTransactionsOnNonWorkingDay()
+ && !WorkingDaysUtil.isWorkingDay(holidayDetailDTO.getWorkingDays(), loan.getDisbursementDate())) {
+ final String errorMessage = "Expected disbursement date cannot be on a non working day";
+ throw new LoanApplicationDateException("disbursement.date.on.non.working.day", errorMessage,
+ loan.getExpectedDisbursedOnLocalDate());
+ }
+ if (!holidayDetailDTO.isAllowTransactionsOnHoliday()
+ && HolidayUtil.isHoliday(loan.getDisbursementDate(), holidayDetailDTO.getHolidays())) {
+ final String errorMessage = "Expected disbursement date cannot be on a holiday";
+ throw new LoanApplicationDateException("disbursement.date.on.holiday", errorMessage,
+ loan.getExpectedDisbursedOnLocalDate());
+ }
+
+ });
+ }
+
+ private static @NotNull BigDecimal collectTotalCollateral(Set<LoanCollateralManagement> loanCollateralManagements) {
+ BigDecimal totalCollateral = BigDecimal.ZERO;
+
+ for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
+ BigDecimal quantity = loanCollateralManagement.getQuantity();
+ BigDecimal pctToBase = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getPctToBase();
+ BigDecimal basePrice = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getBasePrice();
+ totalCollateral = totalCollateral.add(quantity.multiply(basePrice).multiply(pctToBase).divide(BigDecimal.valueOf(100)));
+ }
+ return totalCollateral;
+ }
+
+ private static @NotNull Set<String> getDisbursementParameters(boolean isAccountTransfer) {
+ Set<String> disbursementParameters;
if (isAccountTransfer) {
disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", "dateFormat",
@@ -109,38 +241,7 @@ public final class LoanTransactionValidator {
LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.fixedEmiAmountParameterName,
LoanApiConstants.postDatedChecks, LoanApiConstants.disbursementNetDisbursalAmountParameterName));
}
-
- final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
- this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters);
-
- final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
- final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.disbursement");
-
- final JsonElement element = this.fromApiJsonHelper.parse(json);
- final LocalDate actualDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("actualDisbursementDate", element);
- baseDataValidator.reset().parameter("actualDisbursementDate").value(actualDisbursementDate).notNull();
-
- final String note = this.fromApiJsonHelper.extractStringNamed("note", element);
- baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000);
-
- final BigDecimal principal = this.fromApiJsonHelper
- .extractBigDecimalWithLocaleNamed(LoanApiConstants.principalDisbursedParameterName, element);
- baseDataValidator.reset().parameter(LoanApiConstants.principalDisbursedParameterName).value(principal).ignoreIfNull()
- .positiveAmount();
-
- final BigDecimal netDisbursalAmount = this.fromApiJsonHelper
- .extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName, element);
- baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
- .ignoreIfNull().positiveAmount();
-
- final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName,
- element);
- baseDataValidator.reset().parameter(LoanApiConstants.fixedEmiAmountParameterName).value(emiAmount).ignoreIfNull().positiveAmount()
- .notGreaterThanMax(principal);
-
- validatePaymentDetails(baseDataValidator, element);
-
- throwExceptionIfValidationWarningsExist(dataValidationErrors);
+ return disbursementParameters;
}
public void validateDisbursementWithPostDatedChecks(final String json, final Long loanId) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index aeec4b9a7..df76140dc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -46,6 +46,7 @@ import org.apache.fineract.cob.service.LoanAccountLockService;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
@@ -135,7 +136,6 @@ import org.apache.fineract.portfolio.calendar.exception.CalendarParameterUpdateN
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
-import org.apache.fineract.portfolio.collateralmanagement.exception.LoanCollateralAmountNotSufficientException;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
import org.apache.fineract.portfolio.collectionsheet.command.SingleDisbursalCommand;
@@ -164,6 +164,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
@@ -194,6 +196,7 @@ import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionVa
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.exception.LinkedAccountRequiredException;
+import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -213,6 +216,7 @@ import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
+ private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
private final PlatformSecurityContext context;
private final LoanTransactionValidator loanTransactionValidator;
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
@@ -251,7 +255,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler;
private final PostDatedChecksRepository postDatedChecksRepository;
private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository;
- private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
+ private final LoanLifecycleStateMachine loanLifecycleStateMachine;
private final LoanAccountLockService loanAccountLockService;
private final ExternalIdFactory externalIdFactory;
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
@@ -294,28 +298,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command, Boolean isAccountTransfer,
Boolean isWithoutAutoPayment) {
+ loanTransactionValidator.validateDisbursement(command, isAccountTransfer, loanId);
- final AppUser currentUser = getAppUserIfPresent();
-
- this.loanTransactionValidator.validateDisbursement(command.json(), isAccountTransfer);
-
- if (command.parameterExists("postDatedChecks")) {
- // validate with post dated checks for the disbursement
- this.loanTransactionValidator.validateDisbursementWithPostDatedChecks(command.json(), loanId);
- }
-
- Loan loan = this.loanAssembler.assembleFrom(loanId);
- // Fail fast if client/group is not active or actual loan status disallows disbursal
- checkClientOrGroupActive(loan);
-
- final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
-
- if (loan.isChargedOff() && DateUtils.isBefore(actualDisbursementDate, loan.getChargedOffOnDate())) {
- throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
- + loanId
- + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
- loanId);
- }
+ Loan loan = loanAssembler.assembleFrom(loanId);
if (loan.loanProduct().isDisallowExpectedDisbursements()) {
List<LoanDisbursementDetails> filteredList = loan.getDisbursementDetails().stream()
@@ -323,8 +308,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// Check whether a new LoanDisbursementDetails is required
if (filteredList.isEmpty()) {
// create artificial 'tranche/expected disbursal' as current disburse code expects it for
- // multi-disbursal
- // products
+ // multi-disbursal products
final LocalDate artificialExpectedDate = loan.getExpectedDisbursedOnLocalDate();
LoanDisbursementDetails disbursementDetail = new LoanDisbursementDetails(artificialExpectedDate, null,
loan.getDisbursedAmount(), null, false);
@@ -332,61 +316,23 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.getAllDisbursementDetails().add(disbursementDetail);
}
}
- loan.validateAccountStatus(LoanEvent.LOAN_DISBURSED);
-
- // Get disbursedAmount
- final BigDecimal disbursedAmount = loan.getDisbursedAmount();
- final Set<LoanCollateralManagement> loanCollateralManagements = loan.getLoanCollateralManagements();
-
- // Get relevant loan collateral modules
- if ((loanCollateralManagements != null && !loanCollateralManagements.isEmpty()) && loan.getLoanType().isIndividualAccount()) {
-
- BigDecimal totalCollateral = BigDecimal.valueOf(0);
-
- for (LoanCollateralManagement loanCollateralManagement : loanCollateralManagements) {
- BigDecimal quantity = loanCollateralManagement.getQuantity();
- BigDecimal pctToBase = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getPctToBase();
- BigDecimal basePrice = loanCollateralManagement.getClientCollateralManagement().getCollaterals().getBasePrice();
- totalCollateral = totalCollateral.add(quantity.multiply(basePrice).multiply(pctToBase).divide(BigDecimal.valueOf(100)));
- }
-
- // Validate the loan collateral value against the disbursedAmount
- if (disbursedAmount.compareTo(totalCollateral) > 0) {
- throw new LoanCollateralAmountNotSufficientException(disbursedAmount);
- }
- }
-
- // validate ActualDisbursement Date Against Expected Disbursement Date
- LoanProduct loanProduct = loan.loanProduct();
- if (loanProduct.isSyncExpectedWithDisbursementDate()) {
- syncExpectedDateWithActualDisbursementDate(loan, actualDisbursementDate);
- }
final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling();
final LocalDate rescheduledRepaymentDate = command.localDateValueOfParameterNamed("adjustRepaymentDate");
-
- entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, EntityTables.LOAN.getName(), StatusEnum.DISBURSE.getValue(),
- EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
-
- LocalDate recalculateFrom = null;
+ final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
if (!loan.isMultiDisburmentLoan()) {
loan.setActualDisbursementDate(actualDisbursementDate);
}
- ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
// validate actual disbursement date against meeting date
- final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
- CalendarEntityType.LOANS.getValue());
- if (loan.isSyncDisbursementWithMeeting()) {
- this.loanTransactionValidator.validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance,
- scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), scheduleGeneratorDTO.getNumberOfdays());
- }
+ ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
businessEventNotifierService.notifyPreBusinessEvent(new LoanDisbursalBusinessEvent(loan));
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
+ final AppUser currentUser = getAppUserIfPresent();
final Map<String, Object> changes = new LinkedHashMap<>();
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
@@ -400,12 +346,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// Recalculate first repayment date based in actual disbursement date.
updateLoanCounters(loan, actualDisbursementDate);
Money amountBeforeAdjust = loan.getPrincipal();
- boolean canDisburse = loan.canDisburse(actualDisbursementDate);
- ChangedTransactionDetail changedTransactionDetail = null;
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
- if (canDisburse) {
+ if (loan.canDisburse(actualDisbursementDate)) {
// Get netDisbursalAmount from disbursal screen field.
final BigDecimal netDisbursalAmount = command
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
@@ -441,9 +385,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
amountToDisburse = disburseAmount.minus(loanOutstanding);
-
disburseLoanToLoan(loan, command, loanOutstanding);
}
+
LoanTransaction disbursementTransaction = null;
if (isAccountTransfer) {
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
@@ -463,17 +407,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
*/
recalculateSchedule = true;
}
+
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate,
rescheduledRepaymentDate);
boolean downPaymentEnabled = loan.repaymentScheduleDetail().isEnableDownPayment();
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) {
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
}
- if (isPaymentTypeApplicableForDisbursementCharge) {
- changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
- } else {
- changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
- }
+ ChangedTransactionDetail changedTransactionDetail = disburseLoan(command, isPaymentTypeApplicableForDisbursementCharge,
+ paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO);
loan.adjustNetDisbursalAmount(amountToDisburse.getAmount());
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
@@ -612,6 +554,69 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.build();
}
+ private ChangedTransactionDetail disburseLoan(JsonCommand command, boolean isPaymentTypeApplicableForDisbursementCharge,
+ PaymentDetail paymentDetail, Loan loan, AppUser currentUser, Map<String, Object> changes,
+ ScheduleGeneratorDTO scheduleGeneratorDTO) {
+ final PaymentDetail paymentDetail1 = isPaymentTypeApplicableForDisbursementCharge ? paymentDetail : null;
+ final LocalDate actualDisbursementDate1 = command.localDateValueOfParameterNamed(Loan.ACTUAL_DISBURSEMENT_DATE);
+
+ loan.setDisbursedBy(currentUser);
+ loan.updateLoanScheduleDependentDerivedFields();
+
+ changes.put(Loan.LOCALE, command.locale());
+ changes.put(Loan.DATE_FORMAT, command.dateFormat());
+ changes.put(Loan.ACTUAL_DISBURSEMENT_DATE, command.stringValueOfParameterNamed(Loan.ACTUAL_DISBURSEMENT_DATE));
+
+ boolean disbursementMissedParam = loan.isDisbursementMissed();
+ LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate();
+ if ((loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
+ && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
+ loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
+ }
+
+ loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
+ loan.updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate1);
+ loan.handleDisbursementTransaction(actualDisbursementDate1, paymentDetail1);
+ loan.updateLoanSummaryDerivedFields();
+ final Money interestApplied = Money.of(loan.getCurrency(), loan.getSummary().getTotalInterestCharged());
+
+ /*
+ * Add an interest applied transaction of the interest is accrued upfront (Up front accrual), no accounting or
+ * cash based accounting is selected
+ */
+ if (((loan.isMultiDisburmentLoan() && loan.getDisbursedLoanDisbursementDetails().size() == 1) || !loan.isMultiDisburmentLoan())
+ && loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
+ ExternalId externalId = ExternalId.empty();
+ if (TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled()) {
+ externalId = ExternalId.generate();
+ }
+ final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan, interestApplied,
+ actualDisbursementDate1, externalId);
+ loan.addLoanTransaction(interestAppliedTransaction);
+ }
+
+ ChangedTransactionDetail changedTransactionDetail = null;
+ if (loan.getLoanProduct().isMultiDisburseLoan()) {
+ final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
+ if (!allNonContraTransactionsPostDisbursement.isEmpty()) {
+ final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = transactionProcessorFactory
+ .determineProcessor(loan.getTransactionProcessingStrategyCode());
+ changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loan.getDisbursementDate(),
+ allNonContraTransactionsPostDisbursement, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
+ loan.getActiveCharges());
+ for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+ mapEntry.getValue().updateLoan(loan);
+ }
+ loan.getLoanTransactions().addAll(changedTransactionDetail.getNewTransactionMappings().values());
+ }
+ loan.updateLoanSummaryDerivedFields();
+ }
+
+ loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, loan);
+ changes.put(Loan.PARAM_STATUS, LoanEnumerations.status(loan.getLoanStatus()));
+ return changedTransactionDetail;
+ }
+
private void updatePostDatedChecks(Set<PostDatedChecks> postDatedChecks) {
this.postDatedChecksRepository.saveAll(postDatedChecks);
}
@@ -799,11 +804,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || downPaymentEnabled) {
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
}
- if (configurationDomainService.isPaymentTypeApplicableForDisbursementCharge()) {
- changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, paymentDetail);
- } else {
- changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO, null);
- }
+ changedTransactionDetail = disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(),
+ paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO);
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
@@ -1132,7 +1134,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.updateLoanSummaryDerivedFields();
- doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), defaultLoanLifecycleStateMachine);
+ doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), loanLifecycleStateMachine);
return changedTransactionDetail;
}
@@ -1519,7 +1521,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
- defaultLoanLifecycleStateMachine, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
+ loanLifecycleStateMachine, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
scheduleGeneratorDTO, reversalTxnExternalId);
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
@@ -1681,7 +1683,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction);
- loan.handleChargebackTransaction(newTransaction, defaultLoanLifecycleStateMachine);
+ loan.handleChargebackTransaction(newTransaction, loanLifecycleStateMachine);
loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
@@ -1765,8 +1767,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
- final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
- defaultLoanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
+ final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction, loanLifecycleStateMachine,
+ existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
@@ -1863,7 +1865,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
- final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine, changes,
+ final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, loanLifecycleStateMachine, changes,
existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
@@ -1937,8 +1939,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
- ChangedTransactionDetail changedTransactionDetail = loan.close(command, defaultLoanLifecycleStateMachine, changes,
- existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
+ ChangedTransactionDetail changedTransactionDetail = loan.close(command, loanLifecycleStateMachine, changes, existingTransactionIds,
+ existingReversedTransactionIds, scheduleGeneratorDTO);
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
@@ -2039,7 +2041,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat());
- loan.closeAsMarkedForReschedule(command, defaultLoanLifecycleStateMachine, changes);
+ loan.closeAsMarkedForReschedule(command, loanLifecycleStateMachine, changes);
saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2083,7 +2085,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
private void disburseLoanToLoan(final Loan loan, final JsonCommand command, final BigDecimal amount) {
-
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
@@ -2098,7 +2099,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
private void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
-
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName);
@@ -2118,7 +2118,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanTransactionType.DISBURSEMENT.getValue(), null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null,
txnExternalId, loan, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck);
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
-
}
@Transactional
@@ -2136,7 +2135,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ExternalId externalId = externalIdFactory.create();
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId);
loan.addLoanTransaction(newTransferTransaction);
- LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
+ LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
@@ -2159,7 +2158,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate,
externalId);
loan.addLoanTransaction(newTransferAcceptanceTransaction);
- LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
+ LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
if (loan.getTotalOverpaid() != null) {
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
} else {
@@ -2192,7 +2191,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate,
externalId);
loan.addLoanTransaction(newTransferAcceptanceTransaction);
- LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
+ LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
@@ -2209,7 +2208,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
public void rejectLoanTransfer(final Loan loan) {
this.loanAssembler.setHelpers(loan);
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
- LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine;
+ LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
saveLoanWithDataIntegrityViolationChecks(loan);
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
@@ -2610,7 +2609,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
- ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(defaultLoanLifecycleStateMachine, existingTransactionIds,
+ ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(loanLifecycleStateMachine, existingTransactionIds,
existingReversedTransactionIds, scheduleGeneratorDTO);
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
@@ -2885,8 +2884,44 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate rescheduledRepaymentDate) {
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.fixedEmiAmountParameterName);
- loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount,
- nextPossibleRepaymentDate, rescheduledRepaymentDate);
+
+ boolean isEmiAmountChanged = false;
+ LoanProduct loanProduct = loan.getLoanProduct();
+ if ((loanProduct.isMultiDisburseLoan() || loanProduct.isCanDefineInstallmentAmount()) && emiAmount != null
+ && emiAmount.compareTo(loan.retriveLastEmiAmount()) != 0) {
+ if (loanProduct.isMultiDisburseLoan()) {
+ final LocalDate dateValue = null;
+ final boolean isSpecificToInstallment = false;
+ final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled = scheduleGeneratorDTO
+ .isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
+ LocalDate effectiveDateFrom = actualDisbursementDate;
+ if (!isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled && actualDisbursementDate.equals(nextPossibleRepaymentDate)) {
+ effectiveDateFrom = nextPossibleRepaymentDate.plusDays(1);
+ }
+ LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(),
+ effectiveDateFrom, emiAmount, dateValue, isSpecificToInstallment, loan, LoanStatus.ACTIVE.getValue());
+ loan.getLoanTermVariations().add(loanVariationTerms);
+ } else {
+ loan.setFixedEmiAmount(emiAmount);
+ }
+ isEmiAmountChanged = true;
+ }
+ if (rescheduledRepaymentDate != null && loanProduct.isMultiDisburseLoan()) {
+ final boolean isSpecificToInstallment = false;
+ LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(),
+ nextPossibleRepaymentDate, emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, loan,
+ LoanStatus.ACTIVE.getValue());
+ loan.getLoanTermVariations().add(loanVariationTerms);
+ }
+
+ if (loan.isActualDisbursedOnDateEarlierOrLaterThanExpected(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
+ || rescheduledRepaymentDate != null) {
+ if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+ loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
+ } else {
+ loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
+ }
+ }
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
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 71828af65..18287ad93 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
@@ -346,8 +346,8 @@ public class LoanAccountConfiguration {
@Bean
@ConditionalOnMissingBean(LoanWritePlatformService.class)
- public LoanWritePlatformService loanWritePlatformService(PlatformSecurityContext context,
- LoanTransactionValidator loanTransactionValidator,
+ public LoanWritePlatformService loanWritePlatformService(LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory,
+ PlatformSecurityContext context, LoanTransactionValidator loanTransactionValidator,
LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper,
LoanAccountDomainService loanAccountDomainService, NoteRepository noteRepository,
LoanTransactionRepository loanTransactionRepository, LoanTransactionRelationRepository loanTransactionRelationRepository,
@@ -373,17 +373,18 @@ public class LoanAccountConfiguration {
LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService, ErrorHandler errorHandler,
LoanDownPaymentHandlerService loanDownPaymentHandlerService, AccountTransferRepository accountTransferRepository,
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService) {
- return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
- loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
- loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository,
- paymentDetailWritePlatformService, holidayRepository, configurationDomainService, workingDaysRepository,
- accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService,
- loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService,
- loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
- guarantorDomainService, loanUtilService, loanSummaryWrapper, entityDatatableChecksWritePlatformService,
- transactionProcessingStrategy, codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository,
- repaymentWithPostDatedChecksAssembler, postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository,
- defaultLoanLifecycleStateMachine, loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
+ return new LoanWritePlatformServiceJpaRepositoryImpl(transactionProcessorFactory, context, loanTransactionValidator,
+ loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository,
+ loanTransactionRepository, loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService,
+ calendarInstanceRepository, paymentDetailWritePlatformService, holidayRepository, configurationDomainService,
+ workingDaysRepository, accountTransfersWritePlatformService, accountTransfersReadPlatformService,
+ accountAssociationsReadPlatformService, loanReadPlatformService, fromApiJsonHelper, calendarRepository,
+ loanScheduleHistoryWritePlatformService, loanApplicationValidator, accountAssociationRepository,
+ accountTransferDetailRepository, businessEventNotifierService, guarantorDomainService, loanUtilService, loanSummaryWrapper,
+ entityDatatableChecksWritePlatformService, transactionProcessingStrategy, codeValueRepository,
+ cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
+ postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
+ loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
loanTransactionAssembler, loanAccrualsProcessingService);
}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java
index 727cbdd51..a59677ac3 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentDueBusinessStepTest.java
@@ -95,8 +95,8 @@ public class CheckLoanRepaymentDueBusinessStepTest {
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(null);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
when(loanForProcessing.getCurrency()).thenReturn(currency);
when(repaymentInstallment.getTotalOutstanding(currency)).thenReturn(money);
when(money.isGreaterThanZero()).thenReturn(true);
@@ -153,8 +153,8 @@ public class CheckLoanRepaymentDueBusinessStepTest {
// Loan Product setting overrides global settings
when(loanProduct.getDueDaysForRepaymentEvent()).thenReturn(1);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
when(loanForProcessing.getCurrency()).thenReturn(currency);
when(repaymentInstallment.getTotalOutstanding(currency)).thenReturn(money);
when(money.isGreaterThanZero()).thenReturn(true);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java
index ab8b8ae1e..345e23c33 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/CheckLoanRepaymentOverdueBusinessStepTest.java
@@ -111,8 +111,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallment);
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
when(loanForProcessing.getCurrency()).thenReturn(currency);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
@@ -138,8 +138,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
loanInstallmentRepaymentDueDateBefore5Days, BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0),
BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
// when
@@ -166,8 +166,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
List<LoanRepaymentScheduleInstallment> loanRepaymentScheduleInstallments = Arrays.asList(repaymentInstallmentPaidOff);
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(null);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
@@ -196,8 +196,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
when(loanForProcessing.getStatus()).thenReturn(LoanStatus.ACTIVE);
// product configuration overrides global configuration
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.valueOf(100));
when(loanForProcessing.getCurrency()).thenReturn(currency);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
@@ -216,8 +216,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
Loan loanForProcessing = Mockito.mock(Loan.class);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
when(loanForProcessing.getStatus()).thenReturn(LoanStatus.ACTIVE);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
// when
Loan processedLoan = underTest.execute(loanForProcessing);
// then - No Business Event raised
@@ -240,8 +240,8 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
BigDecimal.valueOf(1.0), BigDecimal.valueOf(0.0), false, new HashSet<>(), BigDecimal.valueOf(0.0)));
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
when(loanProduct.getOverDueDaysForRepaymentEvent()).thenReturn(1);
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
- when(loanForProcessing.getLoanSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary().getTotalOutstanding()).thenReturn(BigDecimal.ONE);
when(loanForProcessing.getCurrency()).thenReturn(currency);
when(loanForProcessing.getRepaymentScheduleInstallments()).thenReturn(loanRepaymentScheduleInstallments);
// when
diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java
index 5e50faa2e..3728a6bd9 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java
@@ -117,7 +117,7 @@ public class LoanRepaymentBusinessEventSerializerTest {
when(loanForProcessing.getId()).thenReturn(1L);
when(loanForProcessing.getAccountNumber()).thenReturn("0001");
when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId"));
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0));
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
when(loanCurrency.getCode()).thenReturn("CODE");
@@ -168,7 +168,7 @@ public class LoanRepaymentBusinessEventSerializerTest {
when(loanForProcessing.getId()).thenReturn(null);
when(loanForProcessing.getAccountNumber()).thenReturn("0001");
when(loanForProcessing.getExternalId()).thenReturn(ExternalIdFactory.produce("externalId"));
- when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanForProcessing.getSummary()).thenReturn(loanSummary);
when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.valueOf(0.0));
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
when(loanCurrency.getCode()).thenReturn("CODE");
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachineTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachineTest.java
index 111893c3e..98034716f 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachineTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachineTest.java
@@ -143,7 +143,7 @@ class DefaultLoanLifecycleStateMachineTest {
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
- Mockito.when(loan.getLoanSummary()).thenReturn(loanSummary);
+ Mockito.when(loan.getSummary()).thenReturn(loanSummary);
Mockito.when(loanSummary.getTotalOutstanding(eq(currency))).thenReturn(one);
// when
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
@@ -163,7 +163,7 @@ class DefaultLoanLifecycleStateMachineTest {
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
- Mockito.when(loan.getLoanSummary()).thenReturn(loanSummary);
+ Mockito.when(loan.getSummary()).thenReturn(loanSummary);
Mockito.when(loanSummary.getTotalOutstanding(currency)).thenReturn(zero);
// when
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java
index 9928f492c..a8e3ffe1c 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java
@@ -130,22 +130,21 @@ public class LoanRepaymentRescheduleAtDisbursementTest {
this.loanTransactionHelper.disburseLoanWithRepaymentReschedule(disbursementDate, loanID, adjustRepaymentDate);
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
- ArrayList<HashMap> loanRepaymnetSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec,
+ ArrayList<HashMap> loanRepaymentSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec,
loanID);
- HashMap firstInstallement = loanRepaymnetSchedule.get(1);
- Map<String, Object> expectedvalues = new HashMap<>(3);
+ HashMap firstInstallment = loanRepaymentSchedule.get(1);
+ Map<String, Object> expectedValues = new HashMap<>(3);
Calendar date = Calendar.getInstance(Utils.getTimeZoneOfTenant());
date.set(2015, Calendar.MARCH, 16);
- expectedvalues.put("dueDate", getDateAsArray(date, 0));
- expectedvalues.put("principalDue", "834.71");
- expectedvalues.put("interestDue", "49.32");
- expectedvalues.put("feeChargesDue", "0");
- expectedvalues.put("penaltyChargesDue", "0");
- expectedvalues.put("totalDueForPeriod", "884.03");
+ expectedValues.put("dueDate", getDateAsArray(date, 0));
+ expectedValues.put("principalDue", "834.71");
+ expectedValues.put("interestDue", "49.32");
+ expectedValues.put("feeChargesDue", "0");
+ expectedValues.put("penaltyChargesDue", "0");
+ expectedValues.put("totalDueForPeriod", "884.03");
// VALIDATE REPAYMENT SCHEDULE
- verifyLoanRepaymentSchedule(firstInstallement, expectedvalues);
-
+ verifyLoanRepaymentSchedule(firstInstallment, expectedValues);
}
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {