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 <[email protected]>
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) {