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 <kristof.jo...@gmail.com>
AuthorDate: Wed Jun 26 14:34:54 2024 +0200

    FINERACT-2090: restructure loan disbursements
    
    more refactors
---
 .../loan/LoanAccountOwnerTransferBusinessStep.java |  14 +-
 .../investor/service/AccountingServiceImpl.java    |   8 +-
 .../LoanAccountOwnerTransferServiceImpl.java       |  12 +-
 .../LoanAccountOwnerTransferBusinessStepTest.java  |  10 +-
 .../LoanAccountOwnerTransferServiceTest.java       |   2 +-
 .../domain/DefaultLoanLifecycleStateMachine.java   |   2 +-
 .../portfolio/loanaccount/domain/Loan.java         | 175 ++--------------
 .../loanaccount/domain/LoanTransaction.java        |  36 ++--
 .../AbstractCumulativeLoanScheduleGenerator.java   |   2 +-
 .../service/LoanArrearsAgingServiceImpl.java       |   2 +-
 .../loan/CheckLoanRepaymentDueBusinessStep.java    |   2 +-
 .../CheckLoanRepaymentOverdueBusinessStep.java     |   3 +-
 ...LoanScheduleCalculationPlatformServiceImpl.java |   4 +-
 .../serialization/LoanTransactionValidator.java    | 171 +++++++++++----
 .../LoanWritePlatformServiceJpaRepositoryImpl.java | 233 ++++++++++++---------
 .../starter/LoanAccountConfiguration.java          |  27 +--
 .../CheckLoanRepaymentDueBusinessStepTest.java     |   8 +-
 .../CheckLoanRepaymentOverdueBusinessStepTest.java |  24 +--
 .../LoanRepaymentBusinessEventSerializerTest.java  |   4 +-
 .../DefaultLoanLifecycleStateMachineTest.java      |   4 +-
 .../LoanRepaymentRescheduleAtDisbursementTest.java |  21 +-
 21 files changed, 379 insertions(+), 385 deletions(-)

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

Reply via email to