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 6d3357e74 FINERACT-1958  - reverse handling of disbursement with down 
payment
6d3357e74 is described below

commit 6d3357e7400b4b56f89c89e38404029b0e415111
Author: Peter Bagrij <[email protected]>
AuthorDate: Sun Sep 10 15:42:13 2023 +0200

    FINERACT-1958
     - reverse handling of disbursement with down payment
---
 .../portfolio/loanaccount/domain/Loan.java         |   41 +-
 .../loanaccount/domain/LoanTransaction.java        |    2 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   10 -
 .../integrationtests/BaseLoanIntegrationTest.java  |  338 ++++
 ...oanDisbursalWithDownPaymentIntegrationTest.java | 1701 ++++++++++++++++++++
 .../common/accounting/JournalEntryHelper.java      |    9 +
 .../common/loans/LoanProductHelper.java            |    4 +
 7 files changed, 2092 insertions(+), 13 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 80b3286c7..f81970b8f 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
@@ -3044,7 +3044,12 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                     ? Boolean.FALSE
                     : Boolean.TRUE;
             
this.loanRepaymentScheduleDetail.setPrincipal(this.approvedPrincipal);
-            if (this.loanProduct.isMultiDisburseLoan()) {
+            // Remove All the Disbursement Details If the Loan Product is 
disabled and exists one
+            if (this.loanProduct().isDisallowExpectedDisbursements() && 
!getDisbursementDetails().isEmpty()) {
+                for (LoanDisbursementDetails disbursementDetail : 
getAllDisbursementDetails()) {
+                    disbursementDetail.reverse();
+                }
+            } else {
                 for (final LoanDisbursementDetails details : 
getDisbursementDetails()) {
                     details.updateActualDisbursementDate(null);
                 }
@@ -5535,6 +5540,16 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
 
     }
 
+    public ChangedTransactionDetail 
recalculateScheduleFromLastTransaction(final ScheduleGeneratorDTO generatorDTO) 
{
+        if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+            regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
+        } else {
+            regenerateRepaymentSchedule(generatorDTO);
+        }
+        return processTransactions();
+
+    }
+
     public ChangedTransactionDetail 
handleRegenerateRepaymentScheduleWithInterestRecalculation(final 
ScheduleGeneratorDTO generatorDTO) {
         regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
         return processTransactions();
@@ -6505,7 +6520,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             }
         }
         reverseExistingTransactionsTillLastDisbursal(lastDisbursalTransaction);
-        loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, 
existingTransactionIds, existingReversedTransactionIds);
+        loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO);
         actualChanges.put("undolastdisbursal", "true");
         actualChanges.put("disbursedAmount", this.getDisbursedAmount());
         updateLoanSummaryDerivedFields();
@@ -6529,6 +6544,28 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                 transaction.reverse();
             }
         }
+        if (isAutoRepaymentForDownPaymentEnabled()) {
+            // identify down-payment amount for the transaction
+            BigDecimal disbursedAmountPercentageForDownPayment = 
this.loanRepaymentScheduleDetail
+                    .getDisbursedAmountPercentageForDownPayment();
+            Money downPaymentMoney = Money.of(getCurrency(),
+                    
MathUtil.percentageOf(lastDisbursalTransaction.getAmount(), 
disbursedAmountPercentageForDownPayment, 19));
+
+            // find the matching down-payment transaction based on date, 
amount and it also must have downpayment
+            // installment linked
+            Optional<LoanTransaction> downPaymentTransaction = 
this.loanTransactions.stream()
+                    .filter(tr -> 
tr.getTransactionDate().equals(lastDisbursalTransaction.getTransactionDate())
+                            && hasAnInstallmentWithDownPayment(tr, 
downPaymentMoney.getAmount()))
+                    .max(Comparator.comparing(LoanTransaction::getId));
+
+            // reverse the down-payment transaction
+            downPaymentTransaction.ifPresent(LoanTransaction::reverse);
+        }
+    }
+
+    private boolean hasAnInstallmentWithDownPayment(LoanTransaction tr, 
BigDecimal amount) {
+        return tr.getAmount().compareTo(amount) == 0 && 
tr.getLoanTransactionToRepaymentScheduleMappings().stream()
+                .anyMatch(mapping -> 
mapping.getLoanRepaymentScheduleInstallment().isDownPayment());
     }
 
     private void updateLoanToLastDisbursalState(LoanDisbursementDetails 
disbursementDetail) {
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 c7b1afd7f..185ed2d30 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
@@ -910,7 +910,7 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom {
     }
 
     public Boolean isAllowTypeTransactionAtTheTimeOfLastUndo() {
-        return isDisbursement() || isAccrual() || isRepaymentAtDisbursement();
+        return isDisbursement() || isAccrual() || isRepaymentAtDisbursement() 
|| isRepayment();
     }
 
     public boolean isAccrualTransaction() {
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 7c3c739f8..e72be1ab3 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
@@ -869,16 +869,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
             final Map<String, Object> accountingBridgeData = 
loan.deriveAccountingBridgeData(currency.getCode(), existingTransactionIds,
                     existingReversedTransactionIds, isAccountTransfer);
             
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
-
-            // Remove All the Disbursement Details If the Loan Product is 
disabled and exists one
-            if (loan.loanProduct().isDisallowExpectedDisbursements() && 
!loan.getDisbursementDetails().isEmpty()) {
-                List<LoanDisbursementDetails> reversedDisbursementDetails = 
new ArrayList<>();
-                for (LoanDisbursementDetails disbursementDetail : 
loan.getAllDisbursementDetails()) {
-                    disbursementDetail.reverse();
-                    reversedDisbursementDetails.add(disbursementDetail);
-                }
-                
this.loanDisbursementDetailsRepository.saveAllAndFlush(reversedDisbursementDetails);
-            }
             
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan,
 existingTransactionIds);
             businessEventNotifierService.notifyPostBusinessEvent(new 
LoanUndoDisbursalBusinessEvent(loan));
         }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
new file mode 100644
index 000000000..bc1b9ebca
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -0,0 +1,338 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static java.lang.Boolean.FALSE;
+import static java.lang.Boolean.TRUE;
+import static 
org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
+import static 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.UUID;
+import lombok.AllArgsConstructor;
+import lombok.ToString;
+import org.apache.fineract.client.models.AllowAttributeOverrides;
+import org.apache.fineract.client.models.BusinessDateRequest;
+import 
org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
+import org.apache.fineract.client.models.PostLoansRequest;
+import org.apache.fineract.client.models.PostLoansResponse;
+import org.apache.fineract.client.util.CallFailedRuntimeException;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.Account;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import 
org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
+import org.apache.fineract.integrationtests.common.loans.LoanProductHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.Assertions;
+
+public abstract class BaseLoanIntegrationTest {
+
+    static {
+        Utils.initializeRESTAssured();
+    }
+
+    protected static final String DATETIME_PATTERN = "dd MMMM yyyy";
+
+    protected final ResponseSpecification requestSpec = 
createResponseSpecification(200);
+    protected final RequestSpecification responseSpec = 
createRequestSpecification();
+
+    protected final AccountHelper accountHelper = new 
AccountHelper(responseSpec, requestSpec);
+    protected final LoanTransactionHelper loanTransactionHelper = new 
LoanTransactionHelper(responseSpec, requestSpec);
+    protected final LoanProductHelper loanProductHelper = new 
LoanProductHelper();
+    protected JournalEntryHelper journalEntryHelper = new 
JournalEntryHelper(responseSpec, requestSpec);
+    protected BusinessDateHelper businessDateHelper = new BusinessDateHelper();
+
+    // asset
+    protected final Account loansReceivableAccount = 
accountHelper.createAssetAccount();
+    protected final Account interestFeeReceivableAccount = 
accountHelper.createAssetAccount();
+    protected final Account suspenseAccount = 
accountHelper.createAssetAccount();
+    // liability
+    protected final Account suspenseClearingAccount = 
accountHelper.createLiabilityAccount();
+    protected final Account overpaymentAccount = 
accountHelper.createLiabilityAccount();
+    // income
+    protected final Account interestIncomeAccount = 
accountHelper.createIncomeAccount();
+    protected final Account feeIncomeAccount = 
accountHelper.createIncomeAccount();
+    protected final Account feeChargeOffAccount = 
accountHelper.createIncomeAccount();
+    protected final Account recoveriesAccount = 
accountHelper.createIncomeAccount();
+    protected final Account interestIncomeChargeOffAccount = 
accountHelper.createIncomeAccount();
+    // expense
+    protected final Account creditLossBadDebtAccount = 
accountHelper.createExpenseAccount();
+    protected final Account creditLossBadDebtFraudAccount = 
accountHelper.createExpenseAccount();
+    protected final Account writtenOffAccount = 
accountHelper.createExpenseAccount();
+    protected final Account goodwillExpenseAccount = 
accountHelper.createExpenseAccount();
+
+    // Loan product with proper accounting setup
+    protected PostLoanProductsRequest 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() {
+        return new 
PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_",
 6))//
+                .shortName(Utils.uniqueRandomStringGenerator("", 4))//
+                .description("Loan Product Description")//
+                .includeInBorrowerCycle(false)//
+                .currencyCode("USD")//
+                .digitsAfterDecimal(2)//
+                .inMultiplesOf(0)//
+                .installmentAmountInMultiplesOf(1)//
+                .useBorrowerCycle(false)//
+                .minPrincipal(100.0)//
+                .principal(1000.0)//
+                .maxPrincipal(10000.0)//
+                .minNumberOfRepayments(1)//
+                .numberOfRepayments(1)//
+                .maxNumberOfRepayments(30)//
+                .isLinkedToFloatingInterestRates(false)//
+                .minInterestRatePerPeriod((double) 0)//
+                .interestRatePerPeriod((double) 0)//
+                .maxInterestRatePerPeriod((double) 0)//
+                .interestRateFrequencyType(2)//
+                .repaymentEvery(30)//
+                .repaymentFrequencyType(0)//
+                .amortizationType(1)//
+                .interestType(0)//
+                .isEqualAmortization(false)//
+                .interestCalculationPeriodType(1)//
+                .transactionProcessingStrategyCode(
+                        
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY)//
+                .daysInYearType(1)//
+                .daysInMonthType(1)//
+                .canDefineInstallmentAmount(true)//
+                .graceOnArrearsAgeing(3)//
+                .overdueDaysForNPA(179)//
+                .accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
+                .principalThresholdForLastInstallment(50)//
+                .allowVariableInstallments(false)//
+                .canUseForTopup(false)//
+                .isInterestRecalculationEnabled(false)//
+                .holdGuaranteeFunds(false)//
+                .multiDisburseLoan(true)//
+                .allowAttributeOverrides(new AllowAttributeOverrides()//
+                        .amortizationType(true)//
+                        .interestType(true)//
+                        .transactionProcessingStrategyCode(true)//
+                        .interestCalculationPeriodType(true)//
+                        .inArrearsTolerance(true)//
+                        .repaymentEvery(true)//
+                        .graceOnPrincipalAndInterestPayment(true)//
+                        .graceOnArrearsAgeing(true))//
+                .allowPartialPeriodInterestCalcualtion(true)//
+                .maxTrancheCount(10)//
+                .outstandingLoanBalance(10000.0)//
+                .charges(Collections.emptyList())//
+                .accountingRule(3)//
+                
.fundSourceAccountId(suspenseClearingAccount.getAccountID().longValue())//
+                
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
+                
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
+                
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
+                
.incomeFromFeeAccountId(feeIncomeAccount.getAccountID().longValue())//
+                
.incomeFromPenaltyAccountId(feeIncomeAccount.getAccountID().longValue())//
+                
.incomeFromRecoveryAccountId(recoveriesAccount.getAccountID().longValue())//
+                
.writeOffAccountId(writtenOffAccount.getAccountID().longValue())//
+                
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
+                
.receivableInterestAccountId(interestFeeReceivableAccount.getAccountID().longValue())//
+                
.receivableFeeAccountId(interestFeeReceivableAccount.getAccountID().longValue())//
+                
.receivablePenaltyAccountId(interestFeeReceivableAccount.getAccountID().longValue())//
+                .dateFormat(DATETIME_PATTERN)//
+                .locale("en_GB")//
+                .disallowExpectedDisbursements(true)//
+                .allowApprovedDisbursedAmountsOverApplied(true)//
+                .overAppliedCalculationType("percentage")//
+                .overAppliedNumber(50)//
+                
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromChargeOffInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
+                
.chargeOffExpenseAccountId(creditLossBadDebtAccount.getAccountID().longValue())//
+                
.chargeOffFraudExpenseAccountId(creditLossBadDebtFraudAccount.getAccountID().longValue())//
+                
.incomeFromChargeOffPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue());
+    }
+
+    private static RequestSpecification createRequestSpecification() {
+        RequestSpecification request = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        request.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        request.header("Fineract-Platform-TenantId", "default");
+        return request;
+    }
+
+    private static ResponseSpecification createResponseSpecification(int 
statusCode) {
+        return new ResponseSpecBuilder().expectStatusCode(statusCode).build();
+    }
+
+    protected void verifyUndoLastDisbursalShallFail(Long loanId, String 
expectedError) {
+        ResponseSpecification errorResponse = new 
ResponseSpecBuilder().expectStatusCode(403).build();
+        LoanTransactionHelper validationErrorHelper = new 
LoanTransactionHelper(this.responseSpec, errorResponse);
+        CallFailedRuntimeException exception = 
assertThrows(CallFailedRuntimeException.class, () -> {
+            validationErrorHelper.undoLastDisbursalLoan(loanId, new 
PostLoansLoanIdRequest());
+        });
+        assertTrue(exception.getMessage().contains(expectedError));
+    }
+
+    protected void verifyNoTransactions(Long loanId) {
+        verifyTransactions(loanId);
+    }
+
+    protected void verifyTransactions(Long loanId, Transaction... 
transactions) {
+        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoan(responseSpec, requestSpec, loanId.intValue());
+        if (transactions == null || transactions.length == 0) {
+            assertNull(loanDetails.getTransactions(), "No transaction is 
expected");
+        } else {
+            Assertions.assertEquals(transactions.length, 
loanDetails.getTransactions().size());
+            DateTimeFormatter dateTimeFormatter = 
DateTimeFormatter.ofPattern(DATETIME_PATTERN);
+            Arrays.stream(transactions).forEach(tr -> {
+                boolean found = loanDetails.getTransactions().stream()
+                        .anyMatch(item -> Objects.equals(item.getAmount(), 
tr.amount) && Objects.equals(item.getType().getValue(), tr.type)
+                                && Objects.equals(item.getDate(), 
LocalDate.parse(tr.date, dateTimeFormatter)));
+                Assertions.assertTrue(found, "Required transaction  not found: 
" + tr);
+            });
+        }
+    }
+
+    protected void disburseLoan(Long loanId, BigDecimal amount, String date) {
+        loanTransactionHelper.disburseLoan(loanId, new 
PostLoansLoanIdRequest().actualDisbursementDate(date).dateFormat(DATETIME_PATTERN)
+                .transactionAmount(amount).locale("en"));
+    }
+
+    protected void verifyJournalEntries(Long loanId, JournalEntry... entries) {
+        GetJournalEntriesTransactionIdResponse journalEntriesForLoan = 
journalEntryHelper.getJournalEntriesForLoan(loanId);
+        Assertions.assertEquals(entries.length, 
journalEntriesForLoan.getPageItems().size());
+        Arrays.stream(entries).forEach(journalEntry -> {
+            boolean found = journalEntriesForLoan.getPageItems().stream()
+                    .anyMatch(item -> Objects.equals(item.getAmount(), 
journalEntry.amount)
+                            && Objects.equals(item.getGlAccountId(), 
journalEntry.account.getAccountID().longValue())
+                            && 
Objects.requireNonNull(item.getEntryType()).getValue().equals(journalEntry.type));
+            Assertions.assertTrue(found, "Required journal entry not found: " 
+ journalEntry);
+        });
+    }
+
+    protected void verifyRepaymentSchedule(Long loanId, Installment... 
installments) {
+        GetLoansLoanIdResponse loanResponse = 
loanTransactionHelper.getLoan(responseSpec, requestSpec, loanId.intValue());
+        DateTimeFormatter dateTimeFormatter = 
DateTimeFormatter.ofPattern(DATETIME_PATTERN);
+
+        Assertions.assertNotNull(loanResponse.getRepaymentSchedule());
+        
Assertions.assertNotNull(loanResponse.getRepaymentSchedule().getPeriods());
+        Assertions.assertEquals(installments.length, 
loanResponse.getRepaymentSchedule().getPeriods().size(),
+                "Expected installments are not matching with the installments 
configured on the loan");
+
+        for (int i = 1; i < installments.length; i++) {
+            if (installments[i].completed == null) { // this is for the 
disbursement
+                Assertions.assertEquals(installments[i].amount,
+                        
loanResponse.getRepaymentSchedule().getPeriods().get(i).getPrincipalLoanBalanceOutstanding());
+            } else {
+                Assertions.assertEquals(installments[i].amount, 
loanResponse.getRepaymentSchedule().getPeriods().get(i).getPrincipalDue());
+            }
+            Assertions.assertEquals(installments[i].completed, 
loanResponse.getRepaymentSchedule().getPeriods().get(i).getComplete());
+            Assertions.assertEquals(LocalDate.parse(installments[i].dueDate, 
dateTimeFormatter),
+                    
loanResponse.getRepaymentSchedule().getPeriods().get(i).getDueDate());
+        }
+    }
+
+    protected void runAt(String date, Runnable runnable) {
+        try {
+            
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(responseSpec, 
requestSpec, 42, true);
+            
GlobalConfigurationHelper.updateIsBusinessDateEnabled(responseSpec, 
requestSpec, TRUE);
+            businessDateHelper.updateBusinessDate(
+                    new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
+            runnable.run();
+        } finally {
+            
GlobalConfigurationHelper.updateIsBusinessDateEnabled(responseSpec, 
requestSpec, FALSE);
+            
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(responseSpec, 
requestSpec, 42, false);
+        }
+    }
+
+    protected Long applyAndApproveLoan(Long clientId, Long loanProductId, 
String loanDisbursementDate, Double amount) {
+        PostLoansResponse postLoansResponse = 
loanTransactionHelper.applyLoan(new PostLoansRequest().clientId(clientId)
+                
.productId(loanProductId).expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN)
+                
.transactionProcessingStrategyCode(DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY)
+                
.locale("en").submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(0)
+                
.interestCalculationPeriodType(1).interestType(0).repaymentFrequencyType(0).repaymentEvery(30).repaymentFrequencyType(0)
+                
.numberOfRepayments(1).loanTermFrequency(30).loanTermFrequencyType(0).maxOutstandingLoanBalance(BigDecimal.valueOf(amount))
+                .principal(BigDecimal.valueOf(amount)).loanType("individual"));
+
+        PostLoansLoanIdResponse approvedLoanResult = 
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
+                new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount)).dateFormat(DATETIME_PATTERN)
+                        .approvedOnDate("01 January 2023").locale("en"));
+
+        return approvedLoanResult.getLoanId();
+    }
+
+    protected void addRepaymentForLoan(Long loanId, Double amount, String 
date) {
+        String firstRepaymentUUID = UUID.randomUUID().toString();
+        loanTransactionHelper.makeLoanRepayment(loanId, new 
PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
+                
.transactionDate(date).locale("en").transactionAmount(amount).externalId(firstRepaymentUUID));
+    }
+
+    protected JournalEntry journalEntry(double amount, Account account, String 
type) {
+        return new JournalEntry(amount, account, type);
+    }
+
+    protected Transaction transaction(double amount, String type, String date) 
{
+        return new Transaction(amount, type, date);
+    }
+
+    protected Installment installment(double amount, Boolean completed, String 
dueDate) {
+        return new Installment(amount, completed, dueDate);
+    }
+
+    @ToString
+    @AllArgsConstructor
+    public static class Transaction {
+
+        Double amount;
+        String type;
+        String date;
+    }
+
+    @ToString
+    @AllArgsConstructor
+    public static class JournalEntry {
+
+        Double amount;
+        Account account;
+        String type;
+    }
+
+    @ToString
+    @AllArgsConstructor
+    public static class Installment {
+
+        Double amount;
+        Boolean completed;
+        String dueDate;
+    }
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/UndoLoanDisbursalWithDownPaymentIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/UndoLoanDisbursalWithDownPaymentIntegrationTest.java
new file mode 100644
index 000000000..f19ee7526
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/UndoLoanDisbursalWithDownPaymentIntegrationTest.java
@@ -0,0 +1,1701 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static java.lang.Boolean.TRUE;
+import static 
org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.math.BigDecimal;
+import org.apache.fineract.client.models.BusinessDateRequest;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
+import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(LoanTestLifecycleExtension.class)
+public class UndoLoanDisbursalWithDownPaymentIntegrationTest extends 
BaseLoanIntegrationTest {
+
+    public static final BigDecimal DOWN_PAYMENT_PERCENTAGE = new 
BigDecimal(25);
+    private final ClientHelper clientHelper = new 
ClientHelper(this.responseSpec, this.requestSpec);
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithSingleDisbursalAutoDowpaymentEnabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
false);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId,
+                    // original entries
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    // original entries reverted
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT")); //
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithSingleDisbursalAutoDowPaymentEnabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
false);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // make a repayment
+            addRepaymentForLoan(loanId, 100.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "01 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId, //
+                    // original entries down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // repayment entries
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // original entries compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // repayment entries compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithSingleDisbursalAutoDowPaymentDisabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
false);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId, //
+                    // original entries
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // original entries are compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT") //
+            );
+
+            // verify repayment entries are reverted
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithSingleDisbursalAutoDowPaymentDisabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
false);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // An extra Manual Repayment after the down-payment
+            addRepaymentForLoan(loanId, 100.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId, //
+                    // original entries
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // original entries compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // manual partial repayment of the first installment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // manual partial repayment of the first installment 
compensation after undoDisburse
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoLastDisbursalForLoanWithSingleDisbursalAutoDowPaymentEnabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
false);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, false, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            verifyUndoLastDisbursalShallFail(loanId, 
"error.msg.loan.product.does.not.support.multiple.disbursals.cannot.undo.last");
+
+        });
+    }
+
+    @Test
+    public void 
testUndoLastDisbursalForLoanWithMultiDisbursalAutoDowPaymentEnabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            verifyUndoLastDisbursalShallFail(loanId, 
"error.msg.tranches.should.be.disbursed.more.than.one.to.undo.last.disbursal");
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalAutoDowPaymentEnabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId, //
+                    // original entries
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    // original entries reverted
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT") //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalAutoDowPaymentEnabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // make a repayment
+            addRepaymentForLoan(loanId, 100.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "01 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId,
+                    // original entries down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // repayment entries
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // original entries compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // repayment entries compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalAutoDowPaymentDisabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023")//
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId,
+                    // original entries
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // original entries are compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT") //
+            );
+
+            // verify repayment entries are reverted
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalAutoDowPaymentDisabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1000.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, installment(1000.0, null, "01 
January 2023"), installment(1000.0, false, "31 January 2023"));
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // An extra Manual Repayment after the down-payment
+            addRepaymentForLoan(loanId, 100.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify that all transactions are reverted
+            verifyNoTransactions(loanId);
+
+            // verify journal entries are compensated after undo disbursal
+            verifyJournalEntries(loanId,
+                    // original entries
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // original entries compensated
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // manual partial repayment of the first installment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // manual partial repayment of the first installment 
compensation after undoDisburse
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(1000.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoLastDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentEnabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, transaction(250.0, "Down Payment", "01 
January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, journalEntry(250.0, 
loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, installment(1000.0, null, "01 
January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Down Payment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            // undoLastDisbursal
+            loanTransactionHelper.undoLastDisbursal(loanId.intValue());
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId,
+                    // first disbursement + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // second disbursement + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // compensation of second disbursement + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoLastDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentDisabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 100.0, "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            // undoLastDisbursal
+            loanTransactionHelper.undoLastDisbursal(loanId.intValue());
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId,
+                    // first disbursement + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // second disbursement + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // compensation of second disbursement + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
+            );
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoLastDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentEnabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Down Payment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("20 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // make an additional repayment after the 2nd disbursal
+            addRepaymentForLoan(loanId, 50.0, "20 January 2023");
+
+            // undo last disbursal shall fail
+            verifyUndoLastDisbursalShallFail(loanId, 
"error.msg.cannot.undo.last.disbursal.after.repayments or waivers");
+        });
+    }
+
+    @Test
+    public void 
testUndoLastDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentDisabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 100.0, "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("20 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // make an additional repayment after the 2nd disbursal
+            addRepaymentForLoan(loanId, 50.0, "20 January 2023");
+
+            // undo last disbursal shall fail
+            verifyUndoLastDisbursalShallFail(loanId, 
"error.msg.cannot.undo.last.disbursal.after.repayments or waivers");
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentEnabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Down Payment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            verifyNoTransactions(loanId);
+
+            // verify journal entries
+            verifyJournalEntries(loanId,
+                    // 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // compensation of the 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of the 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentDisabledAndNoManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 100.0, "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            verifyNoTransactions(loanId);
+
+            // verify journal entries
+            verifyJournalEntries(loanId,
+                    // 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // compensation of the 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of the 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "DEBIT") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentEnabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(true, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Down Payment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Down Payment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("20 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // make an additional repayment after the 2nd disbursal
+            addRepaymentForLoan(loanId, 50.0, "20 January 2023");
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            verifyNoTransactions(loanId);
+
+            // verify journal entries
+            verifyJournalEntries(loanId,
+                    // 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // manual repayment
+                    journalEntry(50.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(50.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of the 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of the 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of repayment
+                    journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(50.0, suspenseClearingAccount, "CREDIT") //
+            );
+        });
+    }
+
+    @Test
+    public void 
testUndoDisbursalForLoanWithMultiDisbursalWith2DisburseAutoDowPaymentDisabledAndHasManualTransactions()
 {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create Loan Product
+            Long loanProductId = createLoanProductWith25PctDownPayment(false, 
true);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 
January 2023", 1500.0);
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            // 1st Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 
2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 250.0, "01 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, //
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(750.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("15 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // 2nd Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(400.0), "15 January 2023");
+
+            // Manual down-payment
+            addRepaymentForLoan(loanId, 100.0, "15 January 2023");
+
+            // verify transactions
+            verifyTransactions(loanId, //
+                    transaction(250.0, "Repayment", "01 January 2023"), //
+                    transaction(1000.0, "Disbursement", "01 January 2023"), //
+                    transaction(100.0, "Repayment", "15 January 2023"), //
+                    transaction(400.0, "Disbursement", "15 January 2023") //
+            );
+
+            // verify journal entries
+            verifyJournalEntries(loanId, journalEntry(250.0, 
loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT") //
+            );
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1000.0, null, "01 January 2023"), //
+                    installment(250.0, true, "01 January 2023"), //
+                    installment(400.0, null, "15 January 2023"), //
+                    installment(100.0, true, "15 January 2023"), //
+                    installment(1050.0, false, "31 January 2023") //
+            );
+
+            businessDateHelper.updateBusinessDate(new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date("20 January 2023")
+                    .dateFormat(DATETIME_PATTERN).locale("en"));
+
+            // make an additional repayment after the 2nd disbursal
+            addRepaymentForLoan(loanId, 50.0, "20 January 2023");
+
+            // undoDisbursal
+            loanTransactionHelper.undoDisbursal(loanId.intValue());
+
+            // Verify Repayment Schedule
+            verifyRepaymentSchedule(loanId, //
+                    installment(1500.0, null, "01 January 2023"), //
+                    installment(1500.0, false, "31 January 2023") //
+            );
+
+            verifyNoTransactions(loanId);
+
+            // verify journal entries
+            verifyJournalEntries(loanId,
+                    // 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "DEBIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "CREDIT"), //
+
+                    // manual repayment
+                    journalEntry(50.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(50.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of the 1st disbursal + down-payment
+                    journalEntry(250.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(250.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(1000.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of the 2nd disbursal + down-payment
+                    journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
+                    journalEntry(400.0, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(400.0, suspenseClearingAccount, "DEBIT"), //
+
+                    // compensation of repayment
+                    journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(50.0, suspenseClearingAccount, "CREDIT") //
+            );
+        });
+    }
+
+    private Long createLoanProductWith25PctDownPayment(boolean 
autoDownPaymentEnabled, boolean multiDisburseEnabled) {
+        PostLoanProductsRequest product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
+        product.setMultiDisburseLoan(multiDisburseEnabled);
+
+        if (!multiDisburseEnabled) {
+            product.disallowExpectedDisbursements(null);
+            product.setAllowApprovedDisbursedAmountsOverApplied(null);
+            product.overAppliedCalculationType(null);
+            product.overAppliedNumber(null);
+        }
+
+        product.setEnableDownPayment(true);
+        
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE);
+        product.setEnableAutoRepaymentForDownPayment(autoDownPaymentEnabled);
+
+        PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
+        GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = 
loanProductHelper
+                .retrieveLoanProductById(loanProductResponse.getResourceId());
+
+        Long loanProductId = loanProductResponse.getResourceId();
+
+        assertEquals(TRUE, 
getLoanProductsProductIdResponse.getEnableDownPayment());
+        
assertNotNull(getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment());
+        assertEquals(0, 
getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE));
+        assertEquals(autoDownPaymentEnabled, 
getLoanProductsProductIdResponse.getEnableAutoRepaymentForDownPayment());
+        assertEquals(multiDisburseEnabled, 
getLoanProductsProductIdResponse.getMultiDisburseLoan());
+        return loanProductId;
+    }
+
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/accounting/JournalEntryHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/accounting/JournalEntryHelper.java
index 155586294..d1b771abd 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/accounting/JournalEntryHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/accounting/JournalEntryHelper.java
@@ -118,4 +118,13 @@ public class JournalEntryHelper {
         return GSON.fromJson(response, 
GetJournalEntriesTransactionIdResponse.class);
     }
 
+    public GetJournalEntriesTransactionIdResponse 
getJournalEntriesForLoan(final Long loanId) {
+        log.info("Getting GL Journal entries for loan id {}", loanId);
+        final String url = "/fineract-provider/api/v1/journalentries?loanId=" 
+ loanId + "&tenantIdentifier=default"
+                + "&orderBy=id&sortOrder=desc&locale=en&dateFormat=dd MMMM 
yyyy";
+        final String response = Utils.performServerGet(this.requestSpec, 
this.responseSpec, url, null);
+        log.info("response {}", response);
+        return GSON.fromJson(response, 
GetJournalEntriesTransactionIdResponse.class);
+    }
+
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
index d5873c82e..b899f20d1 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductHelper.java
@@ -38,6 +38,10 @@ public class LoanProductHelper extends IntegrationTest {
         return 
ok(fineract().loanProducts.retrieveLoanProductDetails1(externalId));
     }
 
+    public GetLoanProductsProductIdResponse retrieveLoanProductById(Long 
loanProductId) {
+        return 
ok(fineract().loanProducts.retrieveLoanProductDetails(loanProductId));
+    }
+
     public PutLoanProductsProductIdResponse 
updateLoanProductByExternalId(String externalId, 
PutLoanProductsProductIdRequest request) {
         return ok(fineract().loanProducts.updateLoanProduct1(externalId, 
request));
     }

Reply via email to