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 fac483272 FINERACT-2090: restructure loan approvals
fac483272 is described below

commit fac4832723746f347a2e192166f542d47ed04835
Author: Kristof Jozsa <[email protected]>
AuthorDate: Wed Jun 19 14:46:37 2024 +0200

    FINERACT-2090: restructure loan approvals
---
 .../domain/ConfigurationDomainService.java         |   2 +-
 .../portfolio/loanaccount/domain/Loan.java         | 129 +-----------
 .../domain/ConfigurationDomainServiceJpa.java      |   2 +-
 .../service/CalendarReadPlatformServiceImpl.java   |   4 +-
 .../CollectionSheetReadPlatformServiceImpl.java    |   2 +-
 .../service/CenterReadPlatformServiceImpl.java     |   2 +-
 .../service/LoanScheduleAssembler.java             | 108 +++++++---
 .../serialization/LoanApplicationValidator.java    | 217 ++++++++++++++++++++-
 ...ationWritePlatformServiceJpaRepositoryImpl.java | 125 ++----------
 .../loanaccount/service/LoanUtilService.java       |   6 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   2 +-
 .../starter/LoanAccountConfiguration.java          |   8 +-
 ...etingWritePlatformServiceJpaRepositoryImpl.java |   6 +-
 .../integrationtests/AccountTransferTest.java      |  12 +-
 .../fineract/integrationtests/BatchApiTest.java    |  16 +-
 .../ClientLoanIntegrationTest.java                 |   4 +-
 .../integrationtests/GroupLoanIntegrationTest.java |   2 +-
 .../LoanAccountsContainsCurrencyFieldTest.java     |  13 +-
 .../LoanApplicationApprovalTest.java               |  22 +--
 .../LoanApplicationUndoLastTrancheTest.java        |   4 +-
 .../LoanAuditingIntegrationTest.java               |   7 +-
 .../LoanCOBAccountLockCatchupInlineCOBTest.java    |  12 +-
 .../LoanCatchUpIntegrationTest.java                |   2 +-
 .../integrationtests/LoanDueCalculationTest.java   |   2 +-
 .../LoanTransactionAuditingIntegrationTest.java    |   7 +-
 .../integrationtests/SchedulerJobsTestResults.java |  52 ++---
 .../integrationtests/cob/CobPartitioningTest.java  |  32 ++-
 .../integrationtests/common/BatchHelper.java       |   6 +-
 .../inlinecob/InlineLoanCOBTest.java               |  12 +-
 .../ExternalAssetOwnerTransferCancelTest.java      |   2 +-
 .../InitiateExternalAssetOwnerTransferTest.java    |   2 +-
 31 files changed, 438 insertions(+), 384 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
index 579b5e232..164986e87 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java
@@ -81,7 +81,7 @@ public interface ConfigurationDomainService {
 
     boolean isPrincipalCompoundingDisabledForOverdueLoans();
 
-    Long retreivePeroidInNumberOfDaysForSkipMeetingDate();
+    Long retreivePeriodInNumberOfDaysForSkipMeetingDate();
 
     boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled();
 
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 a39fd9ebc..cfb680585 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
@@ -113,7 +113,6 @@ import 
org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor.TransactionCtx;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
-import 
org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.InvalidRefundDateException;
@@ -1740,114 +1739,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         return maturityDate;
     }
 
-    public Map<String, Object> loanApplicationApproval(final AppUser 
currentUser, final JsonCommand command,
-            final JsonArray disbursementDataArray, final 
LoanLifecycleStateMachine loanLifecycleStateMachine) {
-        validateAccountStatus(LoanEvent.LOAN_APPROVED);
-
-        final Map<String, Object> actualChanges = new LinkedHashMap<>();
-
-        /*
-         * statusEnum is holding the possible new status derived from 
loanLifecycleStateMachine.transition.
-         */
-
-        final LoanStatus newStatusEnum = 
loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVED, this);
-
-        /*
-         * FIXME: There is no need to check below condition, if 
loanLifecycleStateMachine.transition is doing it's
-         * responsibility properly. Better implementation approach is, if code 
passes invalid combination of states
-         * (fromState and toState), state machine should return invalidate 
state and below if condition should check for
-         * not equal to invalidateState, instead of check new value is same as 
present value.
-         */
-
-        if (!newStatusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
-            loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, 
this);
-            actualChanges.put(PARAM_STATUS, 
LoanEnumerations.status(this.loanStatus));
-
-            // only do below if status has changed in the 'approval' case
-            LocalDate approvedOn = 
command.localDateValueOfParameterNamed(APPROVED_ON_DATE);
-            String approvedOnDateChange = 
command.stringValueOfParameterNamed(APPROVED_ON_DATE);
-            if (approvedOn == null) {
-                approvedOn = 
command.localDateValueOfParameterNamed(EVENT_DATE);
-                approvedOnDateChange = 
command.stringValueOfParameterNamed(EVENT_DATE);
-            }
-
-            LocalDate expectedDisbursementDate = 
command.localDateValueOfParameterNamed(EXPECTED_DISBURSEMENT_DATE);
-
-            BigDecimal approvedLoanAmount = 
command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName);
-            if (approvedLoanAmount != null) {
-                compareApprovedToProposedPrincipal(approvedLoanAmount);
-
-                /*
-                 * All the calculations are done based on the principal 
amount, so it is necessary to set principal
-                 * amount to approved amount
-                 */
-                this.approvedPrincipal = approvedLoanAmount;
-
-                
this.loanRepaymentScheduleDetail.setPrincipal(approvedLoanAmount);
-                
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, 
approvedLoanAmount);
-                
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, 
approvedLoanAmount);
-                
actualChanges.put(LoanApiConstants.disbursementNetDisbursalAmountParameterName, 
netDisbursalAmount);
-
-                if (disbursementDataArray != null) {
-                    updateDisbursementDetails(command, actualChanges);
-                }
-            }
-
-            recalculateAllCharges();
-
-            if (loanProduct.isMultiDisburseLoan()) {
-                List<LoanDisbursementDetails> currentDisbursementDetails = 
getLoanDisbursementDetails();
-
-                if (currentDisbursementDetails.size() > 
loanProduct.maxTrancheCount()) {
-                    final String errorMessage = "Number of tranche shouldn't 
be greater than " + loanProduct.maxTrancheCount();
-                    throw new 
ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, 
errorMessage,
-                            loanProduct.maxTrancheCount(), 
currentDisbursementDetails.size());
-                }
-            }
-            this.approvedOnDate = approvedOn;
-            this.approvedBy = currentUser;
-            actualChanges.put(LOCALE, command.locale());
-            actualChanges.put(DATE_FORMAT, command.dateFormat());
-            actualChanges.put(APPROVED_ON_DATE, approvedOnDateChange);
-
-            final LocalDate submittalDate = this.submittedOnDate;
-            if (DateUtils.isBefore(approvedOn, submittalDate)) {
-                final String errorMessage = "The date on which a loan is 
approved cannot be before its submittal date: " + submittalDate;
-                throw new InvalidLoanStateTransitionException("approval", 
"cannot.be.before.submittal.date", errorMessage,
-                        getApprovedOnDate(), submittalDate);
-            }
-
-            if (expectedDisbursementDate != null) {
-                this.expectedDisbursementDate = expectedDisbursementDate;
-                actualChanges.put(EXPECTED_DISBURSEMENT_DATE, 
this.expectedDisbursementDate);
-
-                if (DateUtils.isBefore(expectedDisbursementDate, approvedOn)) {
-                    final String errorMessage = "The expected disbursement 
date should be either on or after the approval date: "
-                            + approvedOn;
-                    throw new 
InvalidLoanStateTransitionException("expecteddisbursal", 
"should.be.on.or.after.approval.date", errorMessage,
-                            getApprovedOnDate(), expectedDisbursementDate);
-                }
-            }
-
-            
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_APPROVED, 
approvedOn);
-
-            if (DateUtils.isDateInTheFuture(approvedOn)) {
-                final String errorMessage = "The date on which a loan is 
approved cannot be in the future.";
-                throw new InvalidLoanStateTransitionException("approval", 
"cannot.be.a.future.date", errorMessage, getApprovedOnDate());
-            }
-
-            if (this.loanOfficer != null) {
-                final LoanOfficerAssignmentHistory 
loanOfficerAssignmentHistory = LoanOfficerAssignmentHistory.createNew(this,
-                        this.loanOfficer, approvedOn);
-                this.loanOfficerHistory.add(loanOfficerAssignmentHistory);
-            }
-            this.adjustNetDisbursalAmount(this.approvedPrincipal);
-        }
-
-        return actualChanges;
-    }
-
-    private List<LoanDisbursementDetails> getLoanDisbursementDetails() {
+    public List<LoanDisbursementDetails> getLoanDisbursementDetails() {
         List<LoanDisbursementDetails> currentDisbursementDetails = 
getDisbursementDetails();
         if (loanProduct.isDisallowExpectedDisbursements()) {
             if (!currentDisbursementDetails.isEmpty()) {
@@ -1863,24 +1755,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         return currentDisbursementDetails;
     }
 
-    private void compareApprovedToProposedPrincipal(BigDecimal 
approvedLoanAmount) {
-        if (this.loanProduct().isDisallowExpectedDisbursements() && 
this.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
-            BigDecimal maxApprovedLoanAmount = getOverAppliedMax();
-            if (approvedLoanAmount.compareTo(maxApprovedLoanAmount) > 0) {
-                final String errorMessage = "Loan approved amount can't be 
greater than maximum applied loan amount calculation.";
-                throw new InvalidLoanStateTransitionException("approval",
-                        
"amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", 
errorMessage, approvedLoanAmount,
-                        maxApprovedLoanAmount);
-            }
-        } else {
-            if (approvedLoanAmount.compareTo(this.proposedPrincipal) > 0) {
-                final String errorMessage = "Loan approved amount can't be 
greater than loan amount demanded.";
-                throw new InvalidLoanStateTransitionException("approval", 
"amount.can't.be.greater.than.loan.amount.demanded", errorMessage,
-                        this.proposedPrincipal, approvedLoanAmount);
-            }
-        }
-    }
-
+    @Deprecated // moved to LoanApplicationValidator
     private BigDecimal getOverAppliedMax() {
         if 
("percentage".equals(getLoanProduct().getOverAppliedCalculationType())) {
             BigDecimal overAppliedNumber = 
BigDecimal.valueOf(getLoanProduct().getOverAppliedNumber());
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
index 58b0bf3dc..7c2bf1740 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java
@@ -284,7 +284,7 @@ public class ConfigurationDomainServiceJpa implements 
ConfigurationDomainService
     }
 
     @Override
-    public Long retreivePeroidInNumberOfDaysForSkipMeetingDate() {
+    public Long retreivePeriodInNumberOfDaysForSkipMeetingDate() {
         final String propertyName = "skip-repayment-on-first-day-of-month";
         final GlobalConfigurationPropertyData property = 
getGlobalConfigurationPropertyData(propertyName);
         return property.getValue();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java
index 22bee0c55..5c4cb8a26 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java
@@ -249,7 +249,7 @@ public class CalendarReadPlatformServiceImpl implements 
CalendarReadPlatformServ
         Integer numberOfDays = 0;
         boolean isSkipRepaymentOnFirstMonthEnabled = 
this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
         if (isSkipRepaymentOnFirstMonthEnabled) {
-            numberOfDays = 
this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+            numberOfDays = 
this.configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
         }
 
         final Collection<LocalDate> recurringDates = 
CalendarUtils.getRecurringDates(rrule, seedDate, periodStartDate, periodEndDate,
@@ -328,7 +328,7 @@ public class CalendarReadPlatformServiceImpl implements 
CalendarReadPlatformServ
         Integer numberOfDays = 0;
         boolean isSkipRepaymentOnFirstMonthEnabled = 
configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
         if (isSkipRepaymentOnFirstMonthEnabled) {
-            numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+            numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
         }
 
         if (lastMeetingDate != null && 
!calendarData.isBetweenStartAndEndDate(lastMeetingDate)
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java
index 97d7a1489..1c721ac4d 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java
@@ -344,7 +344,7 @@ public class CollectionSheetReadPlatformServiceImpl 
implements CollectionSheetRe
         Integer numberOfDays = 0;
         boolean isSkipRepaymentOnFirstMonthEnabled = 
this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
         if (isSkipRepaymentOnFirstMonthEnabled) {
-            numberOfDays = 
this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+            numberOfDays = 
this.configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
             isSkipMeetingOnFirstDay = 
this.calendarReadPlatformService.isCalendarAssociatedWithEntity(entityId, 
calendar.getId(),
                     entityType.getValue().longValue());
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
index d6fe072d5..9175ef444 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
@@ -510,7 +510,7 @@ public class CenterReadPlatformServiceImpl implements 
CenterReadPlatformService
         Integer numberOfDays = 0;
         boolean isSkipRepaymentOnFirstMonthEnabled = 
this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
         if (isSkipRepaymentOnFirstMonthEnabled) {
-            numberOfDays = 
this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+            numberOfDays = 
this.configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
         }
         for (CenterData centerData : centerDataArray) {
             if 
(centerData.getCollectionMeetingCalendar().isValidRecurringDate(meetingDate, 
isSkipRepaymentOnFirstMonthEnabled,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index f4a7003fd..716015764 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -18,6 +18,13 @@
  */
 package org.apache.fineract.portfolio.loanaccount.loanschedule.service;
 
+import static 
org.apache.fineract.portfolio.loanaccount.domain.Loan.APPROVED_ON_DATE;
+import static 
org.apache.fineract.portfolio.loanaccount.domain.Loan.DATE_FORMAT;
+import static org.apache.fineract.portfolio.loanaccount.domain.Loan.EVENT_DATE;
+import static 
org.apache.fineract.portfolio.loanaccount.domain.Loan.EXPECTED_DISBURSEMENT_DATE;
+import static org.apache.fineract.portfolio.loanaccount.domain.Loan.LOCALE;
+import static 
org.apache.fineract.portfolio.loanaccount.domain.Loan.PARAM_STATUS;
+
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
@@ -36,6 +43,7 @@ import java.util.Set;
 import java.util.TreeSet;
 import lombok.RequiredArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import 
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.ApiParameterError;
@@ -87,7 +95,11 @@ import 
org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanOfficerAssignmentHistory;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
@@ -118,6 +130,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyTy
 import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 import 
org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
+import org.apache.fineract.useradministration.domain.AppUser;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -141,6 +154,8 @@ public class LoanScheduleAssembler {
     private final CalendarInstanceRepository calendarInstanceRepository;
     private final LoanUtilService loanUtilService;
     private final LoanDisbursementDetailsAssembler 
loanDisbursementDetailsAssembler;
+    private final LoanRepositoryWrapper loanRepositoryWrapper;
+    private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
 
     public LoanApplicationTerms assembleLoanTerms(final JsonElement element) {
         final Long loanProductId = 
this.fromApiJsonHelper.extractLongNamed("productId", element);
@@ -228,7 +243,6 @@ public class LoanScheduleAssembler {
 
         LocalDate calculatedRepaymentsStartingFromDate = 
repaymentsStartingFromDate;
 
-        final Boolean synchDisbursement = 
this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", 
element);
         final Long calendarId = 
this.fromApiJsonHelper.extractLongNamed("calendarId", element);
         Calendar calendar = null;
 
@@ -290,19 +304,12 @@ public class LoanScheduleAssembler {
         if (isSkipRepaymentOnFirstMonthEnabled) {
             isSkipMeetingOnFirstDay = 
this.loanUtilService.isLoanRepaymentsSyncWithMeeting(group, calendar);
             if (isSkipMeetingOnFirstDay) {
-                numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
             }
         }
         if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar 
!= null) {
             
validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate,
 calendar, isSkipMeetingOnFirstDay,
                     numberOfDays);
-
-            /*
-             * If disbursement is synced on meeting, make sure disbursement 
date is on a meeting date
-             */
-            if (synchDisbursement != null && synchDisbursement.booleanValue()) 
{
-                
validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar, 
isSkipMeetingOnFirstDay, numberOfDays);
-            }
         }
 
         if 
(RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)) {
@@ -601,20 +608,9 @@ public class LoanScheduleAssembler {
         }
     }
 
-    public void validateDisbursementDateWithMeetingDates(final LocalDate 
expectedDisbursementDate, final Calendar calendar,
-            Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) {
-        // disbursement date should fall on a meeting date
-        if (calendar != null && 
!calendar.isValidRecurringDate(expectedDisbursementDate, 
isSkipRepaymentOnFirstMonth, numberOfDays)) {
-            final String errorMessage = "Expected disbursement date '" + 
expectedDisbursementDate + "' do not fall on a meeting date";
-            throw new 
LoanApplicationDateException("disbursement.date.do.not.match.meeting.date", 
errorMessage, expectedDisbursementDate);
-        }
-
-    }
-
     private void validateRepaymentFrequencyIsSameAsMeetingFrequency(final 
Integer meetingFrequency, final Integer repaymentFrequency,
             final Integer meetingInterval, final Integer repaymentInterval) {
-        // meeting with daily frequency should allow loan products with any
-        // frequency.
+        // meeting with daily frequency should allow loan products with any 
frequency.
         if (!PeriodFrequencyType.DAYS.getValue().equals(meetingFrequency)) {
             // repayment frequency must match with meeting frequency
             if (!meetingFrequency.equals(repaymentFrequency)) {
@@ -886,7 +882,7 @@ public class LoanScheduleAssembler {
         if (isSkipRepaymentOnFirstMonthEnabled) {
             isSkipRepaymentOnFirstMonth = 
this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), 
loanCalendar);
             if (isSkipRepaymentOnFirstMonth) {
-                numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
             }
         }
         final Integer minGap = installmentConfig.getMinimumGap();
@@ -1138,7 +1134,7 @@ public class LoanScheduleAssembler {
             final LocalDate refernceDateForCalculatingFirstRepaymentDate, 
final PeriodFrequencyType repaymentPeriodFrequencyType,
             final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final 
Calendar calendar, final LocalDate submittedOnDate) {
         boolean isMeetingSkipOnFirstDayOfMonth = 
configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
-        int numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+        int numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
         final String frequency = 
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
         final LocalDate derivedFirstRepayment = 
CalendarUtils.getFirstRepaymentMeetingDate(calendar,
                 refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, 
frequency, isMeetingSkipOnFirstDayOfMonth, numberOfDays);
@@ -1384,4 +1380,70 @@ public class LoanScheduleAssembler {
             loanProductRelatedDetail.setEqualAmortization(newValue);
         }
     }
+
+    public Pair<Loan, Map<String, Object>> assembleLoanApproval(AppUser 
currentUser, JsonCommand command, Long loanId) {
+        final JsonArray disbursementDataArray = 
command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
+        final Loan loan = 
this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
+
+        final Map<String, Object> actualChanges = new HashMap<>();
+        defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, 
loan);
+        actualChanges.put(PARAM_STATUS, 
LoanEnumerations.status(loan.getStatus()));
+
+        LocalDate approvedOn = 
command.localDateValueOfParameterNamed(APPROVED_ON_DATE);
+        String approvedOnDateChange = 
command.stringValueOfParameterNamed(APPROVED_ON_DATE);
+        if (approvedOn == null) {
+            approvedOn = command.localDateValueOfParameterNamed(EVENT_DATE);
+            approvedOnDateChange = 
command.stringValueOfParameterNamed(EVENT_DATE);
+        }
+
+        LocalDate expectedDisbursementDate = 
command.localDateValueOfParameterNamed(EXPECTED_DISBURSEMENT_DATE);
+
+        BigDecimal approvedLoanAmount = 
command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName);
+        if (approvedLoanAmount != null) {
+            /*
+             * All the calculations are done based on the principal amount, so 
it is necessary to set principal amount
+             * to approved amount
+             */
+            loan.setApprovedPrincipal(approvedLoanAmount);
+            
loan.getLoanRepaymentScheduleDetail().setPrincipal(approvedLoanAmount);
+            
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, 
approvedLoanAmount);
+            
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, 
approvedLoanAmount);
+            
actualChanges.put(LoanApiConstants.disbursementNetDisbursalAmountParameterName, 
loan.getNetDisbursalAmount());
+
+            if (disbursementDataArray != null) {
+                loan.updateDisbursementDetails(command, actualChanges);
+            }
+        }
+
+        loan.recalculateAllCharges();
+
+        loan.setApprovedOnDate(approvedOn);
+        loan.setApprovedBy(currentUser);
+
+        actualChanges.put(LOCALE, command.locale());
+        actualChanges.put(DATE_FORMAT, command.dateFormat());
+        actualChanges.put(APPROVED_ON_DATE, approvedOnDateChange);
+
+        if (expectedDisbursementDate != null) {
+            loan.setExpectedDisbursementDate(expectedDisbursementDate);
+            actualChanges.put(EXPECTED_DISBURSEMENT_DATE, 
expectedDisbursementDate);
+        }
+
+        if (loan.getLoanOfficer() != null) {
+            final LoanOfficerAssignmentHistory loanOfficerAssignmentHistory = 
LoanOfficerAssignmentHistory.createNew(loan,
+                    loan.getLoanOfficer(), approvedOn);
+            loan.getLoanOfficerHistory().add(loanOfficerAssignmentHistory);
+        }
+
+        loan.adjustNetDisbursalAmount(loan.getApprovedPrincipal());
+
+        if (!actualChanges.isEmpty()) {
+            if 
(actualChanges.containsKey(LoanApiConstants.approvedLoanAmountParameterName)
+                    || actualChanges.containsKey("recalculateLoanSchedule") || 
actualChanges.containsKey("expectedDisbursementDate")) {
+                
loan.regenerateRepaymentSchedule(loanUtilService.buildScheduleGeneratorDTO(loan,
 null));
+            }
+        }
+
+        return Pair.of(loan, actualChanges);
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
index d8d78ed21..0d21be1cc 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
@@ -52,6 +52,9 @@ import 
org.apache.fineract.infrastructure.core.exception.UnsupportedParameterExc
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
+import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
+import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
+import 
org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
 import 
org.apache.fineract.infrastructure.entityaccess.FineractEntityAccessConstants;
 import 
org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessType;
 import 
org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelation;
@@ -68,6 +71,10 @@ import 
org.apache.fineract.organisation.workingdays.domain.WorkingDays;
 import 
org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
 import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
 import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
+import org.apache.fineract.portfolio.calendar.domain.Calendar;
+import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
+import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
+import 
org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
 import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
 import org.apache.fineract.portfolio.client.domain.Client;
 import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
@@ -83,9 +90,12 @@ import 
org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
 import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
@@ -99,6 +109,7 @@ import 
org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementData
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
 import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
 import 
org.apache.fineract.portfolio.loanproduct.domain.AdvancedPaymentAllocationsValidator;
@@ -182,6 +193,11 @@ public final class LoanApplicationValidator {
     private final WorkingDaysRepositoryWrapper workingDaysRepository;
     private final HolidayRepository holidayRepository;
     private final SavingsAccountRepositoryWrapper savingsAccountRepository;
+    private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
+    private final LoanSummaryWrapper loanSummaryWrapper;
+    private final CalendarInstanceRepository calendarInstanceRepository;
+    private final LoanUtilService loanUtilService;
+    private final EntityDatatableChecksWritePlatformService 
entityDatatableChecksWritePlatformService;
 
     public void validateForCreate(final Loan loan) {
         final LocalDate expectedFirstRepaymentOnDate = 
loan.getExpectedFirstRepaymentOnDate();
@@ -238,6 +254,7 @@ public final class LoanApplicationValidator {
         final Long groupId = 
this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.groupIdParameterName, 
element);
         final Client client = clientId != null ? 
this.clientRepository.findOneWithNotFoundDetection(clientId) : null;
         final Group group = groupId != null ? 
this.groupRepository.findOneWithNotFoundDetection(groupId) : null;
+
         validateClientOrGroup(client, group, productId);
 
         validateOrThrow("loan", baseDataValidator -> {
@@ -528,7 +545,7 @@ public final class LoanApplicationValidator {
             // charges
             loanChargeApiJsonValidator.validateLoanCharges(element, 
loanProduct, baseDataValidator);
 
-            /**
+            /*
              * TODO: Add collaterals for other loan accounts if needed. For 
now it's only applicable for individual
              * accounts. (loanType.isJLG() || loanType.isGLIM())
              */
@@ -725,7 +742,7 @@ public final class LoanApplicationValidator {
             }
 
             checkForProductMixRestrictions(element);
-            validateSubmittedOnDate(element, null, loanProduct);
+            validateSubmittedOnDate(element, submittedOnDate, loanProduct);
             validateDisbursementDetails(loanProduct, element);
             validateCollateral(element);
             // validate if disbursement date is a holiday or a non-working day
@@ -1381,7 +1398,6 @@ public final class LoanApplicationValidator {
 
             validateDisbursementDetails(loanProduct, element);
             validateSubmittedOnDate(element, loan.getSubmittedOnDate(), 
loanProduct);
-
             validateClientOrGroup(client, group, productId);
 
             // validate if disbursement date is a holiday or a non-working day
@@ -1439,7 +1455,7 @@ public final class LoanApplicationValidator {
                 }
             }
             if (disbursementDataSize > loanProduct.maxTrancheCount()) {
-                final String errorMessage = "Number of tranche shouldn't be 
greter than " + loanProduct.maxTrancheCount();
+                final String errorMessage = "Number of tranche shouldn't be 
greater than " + loanProduct.maxTrancheCount();
                 throw new 
ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, 
errorMessage,
                         loanProduct.maxTrancheCount(), disbursementDataSize);
             }
@@ -1897,6 +1913,199 @@ public final class LoanApplicationValidator {
         loan.adjustNetDisbursalAmount(netDisbursalAmount);
     }
 
+    public void validateApproval(JsonCommand command, Long loanId) {
+        String json = command.json();
+        if (StringUtils.isBlank(json)) {
+            throw new InvalidJsonException();
+        }
+
+        final Set<String> disbursementParameters = new HashSet<>(
+                Arrays.asList(LoanApiConstants.loanIdTobeApproved, 
LoanApiConstants.approvedLoanAmountParameterName,
+                        LoanApiConstants.approvedOnDateParameterName, 
LoanApiConstants.disbursementNetDisbursalAmountParameterName,
+                        LoanApiConstants.noteParameterName, 
LoanApiConstants.localeParameterName, LoanApiConstants.dateFormatParameterName,
+                        LoanApiConstants.disbursementDataParameterName, 
LoanApiConstants.expectedDisbursementDateParameterName));
+
+        final Type typeOfMap = new TypeToken<Map<String, Object>>() 
{}.getType();
+        this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, 
disbursementParameters);
+
+        validateOrThrow("loanapplication", baseDataValidator -> {
+            final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+            final BigDecimal principal = this.fromApiJsonHelper
+                    
.extractBigDecimalWithLocaleNamed(LoanApiConstants.approvedLoanAmountParameterName,
 element);
+            
baseDataValidator.reset().parameter(LoanApiConstants.approvedLoanAmountParameterName).value(principal).ignoreIfNull()
+                    .positiveAmount();
+
+            final BigDecimal netDisbursalAmount = this.fromApiJsonHelper
+                    
.extractBigDecimalWithLocaleNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName,
 element);
+            
baseDataValidator.reset().parameter(LoanApiConstants.disbursementNetDisbursalAmountParameterName).value(netDisbursalAmount)
+                    .ignoreIfNull().positiveAmount();
+
+            final LocalDate approvedOnDate = 
this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.approvedOnDateParameterName,
+                    element);
+            
baseDataValidator.reset().parameter(LoanApiConstants.approvedOnDateParameterName).value(approvedOnDate).notNull();
+
+            LocalDate expectedDisbursementDate = this.fromApiJsonHelper
+                    
.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, 
element);
+            
baseDataValidator.reset().parameter(LoanApiConstants.expectedDisbursementDateParameterName).value(expectedDisbursementDate)
+                    .ignoreIfNull();
+
+            final String note = 
this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, 
element);
+            
baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
+
+            final Loan loan = 
this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
+            loan.setHelpers(defaultLoanLifecycleStateMachine, 
this.loanSummaryWrapper,
+                    this.loanRepaymentScheduleTransactionProcessorFactory);
+
+            final Client client = loan.client();
+            if (client != null && client.isNotActive()) {
+                throw new ClientNotActiveException(client.getId());
+            }
+            final Group group = loan.group();
+            if (group != null && group.isNotActive()) {
+                throw new GroupNotActiveException(group.getId());
+            }
+
+            if (expectedDisbursementDate == null) {
+                expectedDisbursementDate = 
loan.getExpectedDisbursedOnLocalDate();
+            }
+
+            if (DateUtils.isBefore(approvedOnDate, loan.getSubmittedOnDate())) 
{
+                final String errorMessage = "Loan approval date " + 
approvedOnDate + " can not be before its submittal date: "
+                        + loan.getSubmittedOnDate();
+                throw new InvalidLoanStateTransitionException("approval", 
"cannot.be.before.submittal.date", errorMessage, approvedOnDate,
+                        loan.getSubmittedOnDate());
+            }
+
+            LoanProduct loanProduct = loan.loanProduct();
+            if (loanProduct.isMultiDisburseLoan()) {
+                validateLoanMultiDisbursementDate(element, 
expectedDisbursementDate, principal);
+
+                final JsonArray disbursementDataArray = this.fromApiJsonHelper
+                        
.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName, element);
+                int disbursementDataSize = disbursementDataArray != null ? 
disbursementDataArray.size() : 0;
+                if (disbursementDataSize > loanProduct.maxTrancheCount()) {
+                    final String errorMessage = "Number of tranche shouldn't 
be greater than " + loanProduct.maxTrancheCount();
+                    throw new 
ExceedingTrancheCountException(LoanApiConstants.disbursementDataParameterName, 
errorMessage,
+                            loanProduct.maxTrancheCount(), 
disbursementDataSize);
+                }
+            }
+
+            boolean isSkipRepaymentOnFirstMonth;
+            int numberOfDays = 0;
+            if (loan.isSyncDisbursementWithMeeting() && (loan.isGroupLoan() || 
loan.isJLGLoan())) {
+                Calendar calendar = getCalendarInstance(loan);
+                isSkipRepaymentOnFirstMonth = 
isLoanRepaymentsSyncWithMeeting(loan, calendar);
+                if (isSkipRepaymentOnFirstMonth) {
+                    numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
+                }
+
+                
validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar, 
isSkipRepaymentOnFirstMonth, numberOfDays);
+            }
+
+            
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, 
EntityTables.LOAN.getName(),
+                    StatusEnum.APPROVE.getCode().longValue(), 
EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
+
+            if (loan.isTopup() && loan.getClientId() != null) {
+                validateTopupLoan(loan, expectedDisbursementDate);
+            }
+
+            if (!loan.getStatus().isSubmittedAndPendingApproval()) {
+                final String defaultUserMessage = "Loan Account Approval is 
not allowed. Loan Account is not in submitted and pending approval state.";
+                final ApiParameterError error = ApiParameterError
+                        
.generalError("error.msg.loan.approve.account.is.not.submitted.and.pending.state",
 defaultUserMessage);
+                baseDataValidator.getDataValidationErrors().add(error);
+            }
+
+            BigDecimal approvedLoanAmount = 
command.bigDecimalValueOfParameterNamed(LoanApiConstants.approvedLoanAmountParameterName);
+            if (approvedLoanAmount != null) {
+                compareApprovedToProposedPrincipal(loan, approvedLoanAmount);
+            }
+
+            if (expectedDisbursementDate != null) {
+                if (DateUtils.isBefore(expectedDisbursementDate, 
approvedOnDate)) {
+                    final String errorMessage = "The expected disbursement 
date " + expectedDisbursementDate
+                            + " should be either on or after the approval 
date: " + approvedOnDate;
+                    throw new 
InvalidLoanStateTransitionException("expecteddisbursal", 
"should.be.on.or.after.approval.date", errorMessage,
+                            approvedOnDate, expectedDisbursementDate);
+                }
+            }
+
+            if (client != null && client.getOfficeJoiningDate() != null) {
+                final LocalDate clientOfficeJoiningDate = 
client.getOfficeJoiningDate();
+                if (DateUtils.isBefore(approvedOnDate, 
clientOfficeJoiningDate)) {
+                    throw new InvalidLoanStateTransitionException("approval", 
"cannot.be.before.client.transfer.date",
+                            "The date on which a loan is approved cannot be 
earlier than client's transfer date to this office",
+                            clientOfficeJoiningDate);
+                }
+            }
+
+            if (DateUtils.isDateInTheFuture(approvedOnDate)) {
+                final String errorMessage = "The date on which a loan is 
approved cannot be in the future.";
+                throw new InvalidLoanStateTransitionException("approval", 
"cannot.be.a.future.date", errorMessage, approvedOnDate);
+            }
+
+            final LoanStatus newStatus = 
defaultLoanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVED, loan);
+            if (newStatus.hasStateOf(loan.getStatus())) {
+                final String defaultUserMessage = "Loan is already approved.";
+                final ApiParameterError error = ApiParameterError
+                        
.generalError("error.msg.loan.approve.account.is.not.submitted.and.pending.state",
 defaultUserMessage);
+                baseDataValidator.getDataValidationErrors().add(error);
+            }
+        }); // end validation
+    }
+
+    private void compareApprovedToProposedPrincipal(Loan loan, BigDecimal 
approvedLoanAmount) {
+        if (loan.loanProduct().isDisallowExpectedDisbursements() && 
loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
+            BigDecimal maxApprovedLoanAmount = getOverAppliedMax(loan);
+            if (approvedLoanAmount.compareTo(maxApprovedLoanAmount) > 0) {
+                final String errorMessage = "Loan approved amount can't be 
greater than maximum applied loan amount calculation.";
+                throw new InvalidLoanStateTransitionException("approval",
+                        
"amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", 
errorMessage, approvedLoanAmount,
+                        maxApprovedLoanAmount);
+            }
+        } else {
+            if (approvedLoanAmount.compareTo(loan.getProposedPrincipal()) > 0) 
{
+                final String errorMessage = "Loan approved amount can't be 
greater than loan amount demanded.";
+                throw new InvalidLoanStateTransitionException("approval", 
"amount.can't.be.greater.than.loan.amount.demanded", errorMessage,
+                        loan.getProposedPrincipal(), approvedLoanAmount);
+            }
+        }
+    }
+
+    private BigDecimal getOverAppliedMax(Loan loan) {
+        LoanProduct loanProduct = loan.getLoanProduct();
+        if ("percentage".equals(loanProduct.getOverAppliedCalculationType())) {
+            BigDecimal overAppliedNumber = 
BigDecimal.valueOf(loanProduct.getOverAppliedNumber());
+            BigDecimal totalPercentage = 
BigDecimal.valueOf(1).add(overAppliedNumber.divide(BigDecimal.valueOf(100)));
+            return loan.getProposedPrincipal().multiply(totalPercentage);
+        } else {
+            return 
loan.getProposedPrincipal().add(BigDecimal.valueOf(loanProduct.getOverAppliedNumber()));
+        }
+    }
+
+    /**
+     * validate disbursement date should fall on a meeting date
+     */
+    public void validateDisbursementDateWithMeetingDates(final LocalDate 
expectedDisbursementDate, final Calendar calendar,
+            Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) {
+        if (calendar != null && 
!calendar.isValidRecurringDate(expectedDisbursementDate, 
isSkipRepaymentOnFirstMonth, numberOfDays)) {
+            final String errorMessage = "Expected disbursement date '" + 
expectedDisbursementDate + "' do not fall on a meeting date";
+            throw new 
LoanApplicationDateException("disbursement.date.do.not.match.meeting.date", 
errorMessage, expectedDisbursementDate);
+        }
+    }
+
+    private Calendar getCalendarInstance(Loan loan) {
+        CalendarInstance calendarInstance = 
calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
+                CalendarEntityType.LOANS.getValue());
+        return calendarInstance != null ? calendarInstance.getCalendar() : 
null;
+    }
+
+    private boolean isLoanRepaymentsSyncWithMeeting(Loan loan, Calendar 
calendar) {
+        return 
configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled()
+                && 
loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
+    }
+
     public static void validateOrThrow(String resource, 
Consumer<DataValidatorBuilder> baseDataValidator) {
         final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
         final DataValidatorBuilder dataValidatorBuilder = new 
DataValidatorBuilder(dataValidationErrors).resource(resource);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
index 94aad8ec9..b61f03576 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
@@ -31,23 +31,18 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
-import 
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
-import org.apache.fineract.infrastructure.core.data.ApiParameterError;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import 
org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
-import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
 import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
-import 
org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
-import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
-import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
-import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
 import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
 import 
org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
@@ -85,7 +80,6 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler;
@@ -109,7 +103,6 @@ import 
org.springframework.transaction.annotation.Transactional;
 public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements 
LoanApplicationWritePlatformService {
 
     private final PlatformSecurityContext context;
-    private final FromJsonHelper fromJsonHelper;
     private final LoanApplicationTransitionValidator 
loanApplicationTransitionValidator;
     private final LoanApplicationValidator loanApplicationValidator;
     private final LoanRepositoryWrapper loanRepositoryWrapper;
@@ -121,9 +114,7 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
     private final CalendarInstanceRepository calendarInstanceRepository;
     private final SavingsAccountRepositoryWrapper savingsAccountRepository;
     private final AccountAssociationsRepository accountAssociationsRepository;
-    private final LoanReadPlatformService loanReadPlatformService;
     private final BusinessEventNotifierService businessEventNotifierService;
-    private final ConfigurationDomainService configurationDomainService;
     private final LoanScheduleAssembler loanScheduleAssembler;
     private final LoanUtilService loanUtilService;
     private final CalendarReadPlatformService calendarReadPlatformService;
@@ -510,19 +501,6 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                 .build();
     }
 
-    public void validateMultiDisbursementData(final JsonCommand command, 
LocalDate expectedDisbursementDate) {
-        final String json = command.json();
-        final JsonElement element = this.fromJsonHelper.parse(json);
-
-        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
-        final DataValidatorBuilder baseDataValidator = new 
DataValidatorBuilder(dataValidationErrors).resource("loan");
-        final BigDecimal principal = 
this.fromJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", 
element);
-        loanApplicationValidator.validateLoanMultiDisbursementDate(element, 
baseDataValidator, expectedDisbursementDate, principal);
-        if (!dataValidationErrors.isEmpty()) {
-            throw new PlatformApiDataValidationException(dataValidationErrors);
-        }
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult approveGLIMLoanAppication(final Long 
loanId, final JsonCommand command) {
@@ -572,100 +550,16 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
     @Transactional
     @Override
     public CommandProcessingResult approveApplication(final Long loanId, final 
JsonCommand command) {
-
         final AppUser currentUser = getAppUserIfPresent();
-        LocalDate expectedDisbursementDate = null;
-
-        
this.loanApplicationTransitionValidator.validateApproval(command.json());
-
-        Loan loan = retrieveLoanBy(loanId);
-
-        final JsonArray disbursementDataArray = 
command.arrayOfParameterNamed(LoanApiConstants.disbursementDataParameterName);
-
-        expectedDisbursementDate = 
command.localDateValueOfParameterNamed(LoanApiConstants.expectedDisbursementDateParameterName);
-        if (expectedDisbursementDate == null) {
-            expectedDisbursementDate = loan.getExpectedDisbursedOnLocalDate();
-        }
-        if (loan.loanProduct().isMultiDisburseLoan()) {
-            this.validateMultiDisbursementData(command, 
expectedDisbursementDate);
-        }
-
-        loanApplicationTransitionValidator.checkClientOrGroupActive(loan);
-        Boolean isSkipRepaymentOnFirstMonth = false;
-        Integer numberOfDays = 0;
-        // validate expected disbursement date against meeting date
-        if (loan.isSyncDisbursementWithMeeting() && (loan.isGroupLoan() || 
loan.isJLGLoan())) {
-            final CalendarInstance calendarInstance = 
this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
-                    CalendarEntityType.LOANS.getValue());
-            Calendar calendar = null;
-            if (calendarInstance != null) {
-                calendar = calendarInstance.getCalendar();
-            }
-            // final Calendar calendar = calendarInstance.getCalendar();
-            boolean isSkipRepaymentOnFirstMonthEnabled = 
this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
-            if (isSkipRepaymentOnFirstMonthEnabled) {
-                isSkipRepaymentOnFirstMonth = 
this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
-                if (isSkipRepaymentOnFirstMonth) {
-                    numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
-                }
-            }
-            
this.loanScheduleAssembler.validateDisbursementDateWithMeetingDates(expectedDisbursementDate,
 calendar,
-                    isSkipRepaymentOnFirstMonth, numberOfDays);
-
-        }
+        loanApplicationValidator.validateApproval(command, loanId);
 
-        final Map<String, Object> changes = 
loan.loanApplicationApproval(currentUser, command, disbursementDataArray,
-                defaultLoanLifecycleStateMachine);
-
-        
entityDatatableChecksWritePlatformService.runTheCheckForProduct(loanId, 
EntityTables.LOAN.getName(),
-                StatusEnum.APPROVE.getCode().longValue(), 
EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId());
+        Pair<Loan, Map<String, Object>> loanAndChanges = 
loanScheduleAssembler.assembleLoanApproval(currentUser, command, loanId);
+        final Loan loan = loanAndChanges.getLeft();
+        final Map<String, Object> changes = loanAndChanges.getRight();
 
         if (!changes.isEmpty()) {
-
-            // If loan approved amount less than loan demanded amount, then 
need
-            // to recompute the schedule
-            if 
(changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName) || 
changes.containsKey("recalculateLoanSchedule")
-                    || changes.containsKey("expectedDisbursementDate")) {
-                LocalDate recalculateFrom = null;
-                ScheduleGeneratorDTO scheduleGeneratorDTO = 
this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
-                loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
-            }
-
-            if (loan.isTopup() && loan.getClientId() != null) {
-                final Long loanIdToClose = 
loan.getTopupLoanDetails().getLoanIdToClose();
-                final Loan loanToClose = 
this.loanRepositoryWrapper.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
loan.getClientId());
-                if (loanToClose == null) {
-                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.to.be.closed.with.topup.is.not.active",
-                            "Loan to be closed with this topup is not 
active.");
-                }
-
-                final LocalDate lastUserTransactionOnLoanToClose = 
loanToClose.getLastUserTransactionDate();
-                if (DateUtils.isBefore(loan.getDisbursementDate(), 
lastUserTransactionOnLoanToClose)) {
-                    throw new GeneralPlatformDomainRuleException(
-                            
"error.msg.loan.disbursal.date.should.be.after.last.transaction.date.of.loan.to.be.closed",
-                            "Disbursal date of this loan application " + 
loan.getDisbursementDate()
-                                    + " should be after last transaction date 
of loan to be closed " + lastUserTransactionOnLoanToClose);
-                }
-                BigDecimal loanOutstanding = this.loanReadPlatformService
-                        
.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, 
expectedDisbursementDate).getAmount();
-                final BigDecimal firstDisbursalAmount = 
loan.getFirstDisbursalAmount();
-                if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
-                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
-                            "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
-                }
-                BigDecimal netDisbursalAmount = 
loan.getApprovedPrincipal().subtract(loanOutstanding);
-                loan.adjustNetDisbursalAmount(netDisbursalAmount);
-            }
-
-            loan = loanRepository.saveAndFlush(loan);
-
             final String noteText = 
command.stringValueOfParameterNamed("note");
-            if (StringUtils.isNotBlank(noteText)) {
-                final Note note = Note.loanNote(loan, noteText);
-                changes.put("note", noteText);
-                this.noteRepository.save(note);
-            }
-
+            createNote(noteText, loan).ifPresent(note -> changes.put("note", 
noteText));
             businessEventNotifierService.notifyPostBusinessEvent(new 
LoanApprovedBusinessEvent(loan));
         }
 
@@ -944,10 +838,13 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
         }
     }
 
-    private void createNote(String submittedOnNote, Loan newLoanApplication) {
+    private Optional<Note> createNote(String submittedOnNote, Loan 
newLoanApplication) {
         if (StringUtils.isNotBlank(submittedOnNote)) {
             final Note note = Note.loanNote(newLoanApplication, 
submittedOnNote);
             this.noteRepository.save(note);
+            return Optional.of(note);
+        } else {
+            return Optional.empty();
         }
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
index 824178d49..b0b451f1d 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
@@ -108,7 +108,7 @@ public class LoanUtilService {
         if (isSkipRepaymentOnFirstMonthEnabled) {
             isSkipRepaymentOnFirstMonth = 
isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
             if (isSkipRepaymentOnFirstMonth) {
-                numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
             }
         }
         final Boolean isChangeEmiIfRepaymentDateSameAsDisbursementDateEnabled 
= this.configurationDomainService
@@ -251,7 +251,7 @@ public class LoanUtilService {
                     boolean isSkipRepaymentOnFirstMonthEnabled = 
this.configurationDomainService
                             .isSkippingMeetingOnFirstDayOfMonthEnabled();
                     if (isSkipRepaymentOnFirstMonthEnabled) {
-                        numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                        numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
                         isSkipRepaymentOnFirstMonth = 
isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
                     }
                     calculatedRepaymentsStartingFromDate = 
CalendarUtils.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate,
@@ -279,7 +279,7 @@ public class LoanUtilService {
                 Integer numberOfDays = 0;
                 boolean isSkipRepaymentOnFirstMonthEnabled = 
this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
                 if (isSkipRepaymentOnFirstMonthEnabled) {
-                    numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                    numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
                     isSkipRepaymentOnFirstMonth = 
isLoanRepaymentsSyncWithMeeting(loan.group(), historyList.get(0).getCalendar());
                 }
                 calculatedRepaymentsStartingFromDate = 
CalendarUtils.getNextRepaymentMeetingDate(historyList.get(0).getRecurrence(),
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 b5e91f434..0040243bf 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
@@ -2326,7 +2326,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                 if (isSkipRepaymentOnFirstMonthEnabled) {
                     isSkipRepaymentOnFirstMonth = 
this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar);
                     if (isSkipRepaymentOnFirstMonth) {
-                        numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                        numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
                     }
                 }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
index 89ba8a678..c89b911da 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
@@ -216,12 +216,12 @@ public class LoanAccountConfiguration {
             EntityDatatableChecksWritePlatformService 
entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository 
glimRepository,
             LoanRepository loanRepository, GSIMReadPlatformService 
gsimReadPlatformService,
             LoanLifecycleStateMachine defaultLoanLifecycleStateMachine) {
-        return new 
LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, fromJsonHelper, 
loanApplicationTransitionValidator,
+        return new 
LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, 
loanApplicationTransitionValidator,
                 loanApplicationValidator, loanRepositoryWrapper, 
noteRepository, loanAssembler, loanSummaryWrapper,
                 loanRepaymentScheduleTransactionProcessorFactory, 
calendarRepository, calendarInstanceRepository, savingsAccountRepository,
-                accountAssociationsRepository, loanReadPlatformService, 
businessEventNotifierService, configurationDomainService,
-                loanScheduleAssembler, loanUtilService, 
calendarReadPlatformService, entityDatatableChecksWritePlatformService,
-                glimRepository, loanRepository, gsimReadPlatformService, 
defaultLoanLifecycleStateMachine);
+                accountAssociationsRepository, businessEventNotifierService, 
loanScheduleAssembler, loanUtilService,
+                calendarReadPlatformService, 
entityDatatableChecksWritePlatformService, glimRepository, loanRepository,
+                gsimReadPlatformService, defaultLoanLifecycleStateMachine);
     }
 
     @Bean
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java
index bd6cf4396..1ce2185ad 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java
@@ -100,7 +100,7 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl 
implements MeetingWrit
                     isSkipRepaymentOnFirstMonth = true;
                 }
                 if (isSkipRepaymentOnFirstMonth) {
-                    numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                    numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
                 }
             }
             final Meeting newMeeting = Meeting.createNew(calendarInstance, 
meetingDate, isTransactionDateOnNonMeetingDate,
@@ -229,7 +229,7 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl 
implements MeetingWrit
         if (isSkipRepaymentOnFirstMonthEnabled) {
             if (calendarInstance != null) {
                 isSkipRepaymentOnFirstMonth = true;
-                numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
             }
         }
         final Meeting meetingForUpdate = 
this.meetingRepositoryWrapper.findOneWithNotFoundDetection(command.entityId());
@@ -305,7 +305,7 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl 
implements MeetingWrit
             if (isSkipRepaymentOnFirstMonthEnabled) {
                 isSkipRepaymentOnFirstMonth = true;
                 if (isSkipRepaymentOnFirstMonth) {
-                    numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
+                    numberOfDays = 
configurationDomainService.retreivePeriodInNumberOfDaysForSkipMeetingDate().intValue();
                 }
             }
             // create new meeting
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountTransferTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountTransferTest.java
index f12404848..8d61a0b85 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountTransferTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AccountTransferTest.java
@@ -76,9 +76,9 @@ public class AccountTransferTest {
     public static final String TO_LOAN_ACCOUNT_TYPE = "1";
     public static final String TO_SAVINGS_ACCOUNT_TYPE = "2";
 
-    public static final String LOAN_APPROVAL_DATE = "01 March 2013";
-    public static final String LOAN_APPROVAL_DATE_PLUS_ONE = "02 March 2013";
-    public static final String LOAN_DISBURSAL_DATE = "01 March 2013";
+    public static final String LOAN_APPROVAL_DATE = "10 January 2013";
+    public static final String LOAN_APPROVAL_DATE_PLUS_ONE = "11 January 2013";
+    public static final String LOAN_DISBURSAL_DATE = "10 January 2013";
 
     private ResponseSpecification responseSpec;
     private RequestSpecification requestSpec;
@@ -107,12 +107,12 @@ public class AccountTransferTest {
 
         List<HashMap> financialActivities = 
this.financialActivityAccountHelper.getAllFinancialActivityAccounts(this.responseSpec);
         if (financialActivities.isEmpty()) {
-            /** Setup liability transfer account **/
-            /** Create a Liability and an Asset Transfer Account **/
+            /* Setup liability transfer account **/
+            /* Create a Liability and an Asset Transfer Account **/
             liabilityTransferAccount = accountHelper.createLiabilityAccount();
             Assertions.assertNotNull(liabilityTransferAccount);
 
-            /*** Create A Financial Activity to Account Mapping **/
+            /* Create A Financial Activity to Account Mapping **/
             financialActivityAccountId = (Integer) 
financialActivityAccountHelper.createFinancialActivityAccount(
                     
FinancialActivityAccountsTest.LIABILITY_TRANSFER_FINANCIAL_ACTIVITY_ID, 
liabilityTransferAccount.getAccountID(),
                     responseSpec, CommonConstants.RESPONSE_RESOURCE_ID);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
index fd2398012..0b274ff7f 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java
@@ -573,10 +573,10 @@ public class BatchApiTest {
 
         final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
 
-        final BatchRequest approveLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
+        final BatchRequest approveLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10), 
"approve");
 
-        final BatchRequest disburseLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
+        final BatchRequest disburseLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(8), 
"disburse");
 
         final BatchRequest getLoanRequest = 
BatchHelper.getLoanByExternalIdRequest(getLoanRequestId, approveLoanRequestId,
@@ -2348,10 +2348,10 @@ public class BatchApiTest {
 
         final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
 
-        final BatchRequest approveLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
+        final BatchRequest approveLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10), 
"approve");
 
-        final BatchRequest disburseLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
+        final BatchRequest disburseLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(8), 
"disburse");
 
         final BatchRequest updateLoanRequest = 
BatchHelper.modifyLoanByExternalIdRequest(updateLoanRequestId, 
approveLoanRequestId);
@@ -2414,10 +2414,10 @@ public class BatchApiTest {
 
         final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
 
-        final BatchRequest approveLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
+        final BatchRequest approveLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10), 
"approve");
 
-        final BatchRequest disburseLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
+        final BatchRequest disburseLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(8), 
"disburse");
 
         final BatchRequest getLoanRequest = 
BatchHelper.getLoanByExternalIdRequest(getLoanRequestId, approveLoanRequestId,
@@ -2496,10 +2496,10 @@ public class BatchApiTest {
 
         final BatchRequest applyLoanRequest = 
BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, 
productId);
 
-        final BatchRequest approveLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
+        final BatchRequest approveLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(approveLoanRequestId, 
applyLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10), 
"approve");
 
-        final BatchRequest disburseLoanRequest = 
BatchHelper.transistionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
+        final BatchRequest disburseLoanRequest = 
BatchHelper.transitionLoanStateByExternalId(disburseLoanRequestId, 
approveLoanRequestId,
                 LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(8), 
"disburse");
 
         final BatchRequest getLoanRequest = 
BatchHelper.getLoanByExternalIdRequest(getLoanRequestId, approveLoanRequestId,
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
index c7150d369..348b8b9be 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
@@ -445,7 +445,7 @@ public class ClientLoanIntegrationTest {
 
         DateFormat dateFormat = new SimpleDateFormat(DATETIME_PATTERN, 
Locale.US);
         Calendar todaysDate = 
Calendar.getInstance(Utils.getTimeZoneOfTenant());
-        final String LOAN_DISBURSEMENT_DATE = 
dateFormat.format(todaysDate.getTime());
+        final String LOAN_DISBURSEMENT_DATE = "2 June 2014";
 
         LOG.info("-----------------------------------APPROVE 
LOAN-----------------------------------------");
         loanStatusHashMap = 
LOAN_TRANSACTION_HELPER.approveLoan(LOAN_DISBURSEMENT_DATE, loanID);
@@ -1545,7 +1545,7 @@ public class ClientLoanIntegrationTest {
      * amount plus interest
      */
     @Test
-    public void 
loanWithCahargesOfTypeAmountPlusInterestPercentageAndCashBasedAccountingEnabled()
 {
+    public void 
loanWithChargesOfTypeAmountPlusInterestPercentageAndCashBasedAccountingEnabled()
 {
 
         final Integer clientID = ClientHelper.createClient(REQUEST_SPEC, 
RESPONSE_SPEC);
         ClientHelper.verifyClientCreatedOnServer(REQUEST_SPEC, RESPONSE_SPEC, 
clientID);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/GroupLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/GroupLoanIntegrationTest.java
index 4ca54a512..0bfe3501c 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/GroupLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/GroupLoanIntegrationTest.java
@@ -101,7 +101,7 @@ public class GroupLoanIntegrationTest {
         LOG.info("LoanId : {} ", loanId);
 
         List<Map<String, Object>> approvalFormData = new ArrayList<>();
-        approvalFormData.add(approvalFormData(loanId, "22 September 2011"));
+        approvalFormData.add(approvalFormData(loanId, "20 September 2011"));
 
         HashMap loanStatusHashMap = 
this.loanTransactionHelper.approveGlimAccount(this.requestSpec, 
this.responseSpec, approvalFormData,
                 glimId);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountsContainsCurrencyFieldTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountsContainsCurrencyFieldTest.java
index 4ee2cc95e..caa85a2bf 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountsContainsCurrencyFieldTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountsContainsCurrencyFieldTest.java
@@ -23,9 +23,6 @@ import io.restassured.builder.ResponseSpecBuilder;
 import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
 import java.util.Set;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
@@ -65,15 +62,7 @@ public class LoanAccountsContainsCurrencyFieldTest {
 
     @Test
     public void testGetClientLoanAccountsUsingExternalIdContainsCurrency() {
-
-        // Get today's date
-        LocalDate today = LocalDate.now(ZoneId.systemDefault());
-
-        // Define a custom date formatter
-        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM 
yyyy");
-
-        // Format today's date using the custom formatter
-        String formattedDate = today.format(formatter);
+        String formattedDate = "01 September 2022";
 
         // given
         GlobalConfigurationHelper.manageConfigurations(requestSpec, 
responseSpec,
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationApprovalTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationApprovalTest.java
index 6b37a3043..0f24bdf64 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationApprovalTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationApprovalTest.java
@@ -50,7 +50,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
 @Slf4j
 public class LoanApplicationApprovalTest {
 
-    private static final String DATETIME_PATTERN = "dd MMMM yyyy";
     private ResponseSpecification responseSpec;
     private ResponseSpecification responseSpecForStatusCode403;
     private ResponseSpecification responseSpecForStatusCode400;
@@ -101,7 +100,7 @@ public class LoanApplicationApprovalTest {
 
         final String proposedAmount = "5000";
         final String approvalAmount = "9000";
-        final String approveDate = "20 September 2011";
+        final String approveDate = "2 April 2012";
 
         final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, "01 January 2012");
         final Integer loanProductID = 
this.loanTransactionHelper.getLoanProductId(new 
LoanProductTestBuilder().build(null));
@@ -131,7 +130,6 @@ public class LoanApplicationApprovalTest {
 
     @Test
     public void loanApplicationApprovalAndValidationForMultiDisburseLoans() {
-
         List<HashMap> createTranches = new ArrayList<>();
         createTranches.add(createTrancheDetail("01 March 2014", "1000"));
         createTranches.add(createTrancheDetail("23 March 2014", "4000"));
@@ -139,14 +137,16 @@ public class LoanApplicationApprovalTest {
         final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, "01 January 2014");
         log.info("---------------------------------CLIENT CREATED WITH 
ID--------------------------------------------------- {}", clientID);
 
-        final Integer loanProductID = this.loanTransactionHelper
-                .getLoanProductId(new 
LoanProductTestBuilder().withInterestTypeAsDecliningBalance().withTranches(true)
-                        
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).build(null));
+        final Integer loanProductID = 
this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder() //
+                .withInterestTypeAsDecliningBalance() //
+                .withTranches(true) //
+                .withInterestCalculationPeriodTypeAsRepaymentPeriod(true) //
+                .build(null));
         log.info("----------------------------------LOAN PRODUCT CREATED WITH 
ID------------------------------------------- {}",
                 loanProductID);
 
-        this.trancheLoansApprovedAmountLesserThanProposedAmount(clientID, 
loanProductID, createTranches);
-        this.trancheLoansApprovalValidation(clientID, loanProductID, 
createTranches);
+        trancheLoansApprovedAmountLesserThanProposedAmount(clientID, 
loanProductID, createTranches);
+        trancheLoansApprovalValidation(clientID, loanProductID, 
createTranches);
     }
 
     @Test
@@ -266,12 +266,12 @@ public class LoanApplicationApprovalTest {
                 error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
 
         /* If tranches are not specified for a multi-disburse loan */
-        /**
+        /*
          * error = 
this.loanTransactionHelper.approveLoanForTranches(approveDate, 
expectedDisbursementDate,
          * approvalAmount5, loanID, approveTranche5, 
CommonConstants.RESPONSE_ERROR);
          * assertEquals("error.msg.disbursementData.required",
          * error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
-         **/
+         */
     }
 
     private Integer applyForLoanApplication(final Integer clientID, final 
Integer loanProductID, final String proposedAmount) {
@@ -285,7 +285,7 @@ public class LoanApplicationApprovalTest {
 
         final String loanApplication = new 
LoanApplicationTestBuilder().withPrincipal(proposedAmount).withLoanTermFrequency("5")
                 
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("5").withRepaymentEveryAfter("1")
-                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withExpectedDisbursementDate("04
 April 2012")
+                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withExpectedDisbursementDate("20
 September 2012")
                 .withCollaterals(collaterals).withSubmittedOnDate("02 April 
2012")
                 .build(clientID.toString(), loanProductID.toString(), null);
         return this.loanTransactionHelper.getLoanId(loanApplication);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationUndoLastTrancheTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationUndoLastTrancheTest.java
index 39e9271d0..23d04cfb0 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationUndoLastTrancheTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanApplicationUndoLastTrancheTest.java
@@ -70,7 +70,6 @@ public class LoanApplicationUndoLastTrancheTest {
 
     @Test
     public void loanApplicationUndoLastTranche() {
-
         final String proposedAmount = "5000";
         final String approvalAmount = "2000";
         final String approveDate = "01 March 2014";
@@ -139,8 +138,7 @@ public class LoanApplicationUndoLastTrancheTest {
 
     @Test
     public void loanApplicationUndoLastTrancheToClose() {
-        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
-        LocalDate transactionDate = LocalDate.of(todaysDate.getYear(), 1, 1);
+        LocalDate transactionDate = LocalDate.of(2014, 3, 1);
         String operationDate = Utils.dateFormatter.format(transactionDate);
         LOG.info("Operation date {}", transactionDate);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
index f36d51827..1ff92e392 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAuditingIntegrationTest.java
@@ -98,7 +98,7 @@ public class LoanAuditingIntegrationTest {
         OffsetDateTime now = Utils.getAuditDateTimeToCompare();
 
         final Integer loanID = 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, 
Collections.emptyList(),
-                null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, 
"10 July 2022");
+                null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, 
"10 July 2022", "11 July 2022");
         Assertions.assertNotNull(loanID);
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
@@ -143,7 +143,8 @@ public class LoanAuditingIntegrationTest {
     }
 
     private Integer 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(final Integer clientID, 
final Integer loanProductID,
-            List<HashMap> charges, final String savingsId, String principal, 
final String repaymentStrategy, final String submittedOnDate) {
+            List<HashMap> charges, final String savingsId, String principal, 
final String repaymentStrategy, final String submittedOnDate,
+            final String disbursementDate) {
         LOG.info("--------------------------------APPLYING FOR LOAN 
APPLICATION--------------------------------");
 
         final String loanApplicationJSON = new LoanApplicationTestBuilder() //
@@ -157,7 +158,7 @@ public class LoanAuditingIntegrationTest {
                 .withAmortizationTypeAsEqualInstallments() //
                 .withInterestTypeAsFlatBalance() //
                 .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
-                .withExpectedDisbursementDate(submittedOnDate) //
+                .withExpectedDisbursementDate(disbursementDate) //
                 .withSubmittedOnDate(submittedOnDate) //
                 .withRepaymentStrategy(repaymentStrategy) //
                 .withCharges(charges).build(clientID.toString(), 
loanProductID.toString(), savingsId);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBAccountLockCatchupInlineCOBTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBAccountLockCatchupInlineCOBTest.java
index d4fdbde03..f68accb80 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBAccountLockCatchupInlineCOBTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCOBAccountLockCatchupInlineCOBTest.java
@@ -94,7 +94,7 @@ public class LoanCOBAccountLockCatchupInlineCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -150,7 +150,7 @@ public class LoanCOBAccountLockCatchupInlineCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -207,7 +207,7 @@ public class LoanCOBAccountLockCatchupInlineCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -279,7 +279,7 @@ public class LoanCOBAccountLockCatchupInlineCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -338,7 +338,7 @@ public class LoanCOBAccountLockCatchupInlineCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -393,7 +393,7 @@ public class LoanCOBAccountLockCatchupInlineCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCatchUpIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCatchUpIntegrationTest.java
index 5aa7e3736..62f5d962b 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCatchUpIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCatchUpIntegrationTest.java
@@ -97,7 +97,7 @@ public class LoanCatchUpIntegrationTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDueCalculationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDueCalculationTest.java
index 6700364f7..bb1b9284a 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDueCalculationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDueCalculationTest.java
@@ -119,7 +119,7 @@ public class LoanDueCalculationTest extends 
BaseLoanIntegrationTest {
                     installment(250.0, false, "30 May 2024")) //
             ;
 
-            
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), 
approveLoanRequest(1000.0, "31 January 2024"));
+            
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), 
approveLoanRequest(1000.0, "30 January 2024"));
 
             verifyRepaymentSchedule(postLoansResponse.getLoanId(), 
installment(1000.0, null, "30 January 2024"), //
                     installment(250.0, false, "29 February 2024"), //
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
index c1a4e5f47..161947b8b 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java
@@ -96,7 +96,7 @@ public class LoanTransactionAuditingIntegrationTest {
                 expenseAccount, overpaymentAccount);
 
         final Integer loanID = 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, 
Collections.emptyList(),
-                null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, 
"10 July 2022");
+                null, "10000", LoanApplicationTestBuilder.DEFAULT_STRATEGY, 
"10 July 2022", "12 July 2022");
         Assertions.assertNotNull(loanID);
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
@@ -151,7 +151,8 @@ public class LoanTransactionAuditingIntegrationTest {
     }
 
     private Integer 
applyForLoanApplicationWithPaymentStrategyAndPastMonth(final Integer clientID, 
final Integer loanProductID,
-            List<HashMap> charges, final String savingsId, String principal, 
final String repaymentStrategy, final String submittedOnDate) {
+            List<HashMap> charges, final String savingsId, String principal, 
final String repaymentStrategy, final String submittedOnDate,
+            final String disbursementDate) {
         LOG.info("--------------------------------APPLYING FOR LOAN 
APPLICATION--------------------------------");
 
         final String loanApplicationJSON = new LoanApplicationTestBuilder() //
@@ -165,7 +166,7 @@ public class LoanTransactionAuditingIntegrationTest {
                 .withAmortizationTypeAsEqualInstallments() //
                 .withInterestTypeAsFlatBalance() //
                 .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
-                .withExpectedDisbursementDate(submittedOnDate) //
+                .withExpectedDisbursementDate(disbursementDate) //
                 .withSubmittedOnDate(submittedOnDate) //
                 .withRepaymentStrategy(repaymentStrategy) //
                 .withCharges(charges).build(clientID.toString(), 
loanProductID.toString(), savingsId);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
index f16e3cf19..797baefe0 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
@@ -103,6 +103,9 @@ public class SchedulerJobsTestResults {
     private static final String TO_ACCOUNT_TYPE_SAVINGS = "2";
     private static final String DATE_OF_JOINING = "01 January 2011";
     private static final String TRANSACTION_DATE = "01 March 2013";
+    public static final String LOAN_APPROVAL_DATE = "01 March 2013";
+    public static final String LOAN_APPROVAL_DATE_PLUS_ONE = "02 March 2013";
+    public static final String LOAN_DISBURSAL_DATE = "01 March 2013";
     private static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
     private static final String MINIMUM_OPENING_BALANCE = "1000";
     private static final Float SP_BALANCE = 
Float.valueOf(MINIMUM_OPENING_BALANCE);
@@ -263,14 +266,13 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = createLoanProduct(null);
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), savingsId.toString(),
-                "10 January 2013");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), savingsId.toString(), "1 March 2013");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
 
-        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(AccountTransferTest.LOAN_APPROVAL_DATE, 
loanID);
+        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(LOAN_APPROVAL_DATE, loanID);
         LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
 
         Integer specifiedDueDateChargeId = 
ChargesHelper.createCharges(requestSpec, responseSpec,
@@ -283,7 +285,7 @@ public class SchedulerJobsTestResults {
         Assertions.assertEquals(1, chargesPendingState.size());
 
         String loanDetails = 
this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
-        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(AccountTransferTest.LOAN_DISBURSAL_DATE,
 loanID,
+        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(LOAN_DISBURSAL_DATE,
 loanID,
                 
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
         final HashMap summaryBefore = 
this.savingsAccountHelper.getSavingsSummary(savingsId);
@@ -315,17 +317,17 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = createLoanProduct(null);
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "01 January 2013");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "01 March 2013");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
 
-        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(AccountTransferTest.LOAN_APPROVAL_DATE, 
loanID);
+        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(LOAN_APPROVAL_DATE, loanID);
         LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
 
         String loanDetails = 
this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
-        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(AccountTransferTest.LOAN_DISBURSAL_DATE,
 loanID,
+        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(LOAN_DISBURSAL_DATE,
 loanID,
                 
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
 
@@ -656,17 +658,17 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = createLoanProduct(null);
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "10 January 2013");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "1 March 2013");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
 
-        loanStatusHashMap = 
loanTransactionHelper.approveLoan(AccountTransferTest.LOAN_APPROVAL_DATE, 
loanID);
+        loanStatusHashMap = 
loanTransactionHelper.approveLoan(LOAN_APPROVAL_DATE, loanID);
         LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
 
         String loanDetails = loanTransactionHelper.getLoanDetails(requestSpec, 
responseSpec, loanID);
-        loanStatusHashMap = 
loanTransactionHelper.disburseLoanWithNetDisbursalAmount(AccountTransferTest.LOAN_DISBURSAL_DATE,
 loanID,
+        loanStatusHashMap = 
loanTransactionHelper.disburseLoanWithNetDisbursalAmount(LOAN_DISBURSAL_DATE, 
loanID,
                 
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
 
@@ -786,7 +788,7 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "10 January 2020");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "1 March 2020");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -832,7 +834,7 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = 
createLoanProductNoInterest(overdueFeeChargeId.toString());
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = 
applyForLoanApplicationNoInterest(clientID.toString(), 
loanProductID.toString(), null, "10 January 2020");
+        final Integer loanID = 
applyForLoanApplicationNoInterest(clientID.toString(), 
loanProductID.toString(), null, "01 March 2020");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -883,7 +885,7 @@ public class SchedulerJobsTestResults {
             List<Integer> loanIDs = new ArrayList<>();
             HashMap loanStatusHashMap;
             for (int i = 0; i < 3; i++) {
-                final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+                final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
                 Assertions.assertNotNull(loanID);
 
@@ -946,7 +948,7 @@ public class SchedulerJobsTestResults {
             final Integer loanProductID = 
createLoanProductWithPeriodicAccrual(null);
             Assertions.assertNotNull(loanProductID);
 
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
June 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -1011,7 +1013,7 @@ public class SchedulerJobsTestResults {
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
 
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
July 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -1068,7 +1070,7 @@ public class SchedulerJobsTestResults {
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
 
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2019");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2019");
 
             Assertions.assertNotNull(loanID);
 
@@ -1129,7 +1131,7 @@ public class SchedulerJobsTestResults {
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
             // Test penalty where there is 1 day grace period
-            final Integer loanID2 = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID2 = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
April 2020");
 
             Assertions.assertNotNull(loanID2);
 
@@ -1186,18 +1188,18 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "10 January 2013");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "1 March 2013");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
 
-        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(AccountTransferTest.LOAN_APPROVAL_DATE, 
loanID);
+        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(LOAN_APPROVAL_DATE, loanID);
         LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
 
         String loanDetails = 
this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
-        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(AccountTransferTest.LOAN_APPROVAL_DATE_PLUS_ONE,
-                loanID, 
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(LOAN_APPROVAL_DATE_PLUS_ONE,
 loanID,
+                
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
 
         String JobName = "Apply penalty to overdue loans";
@@ -1238,18 +1240,18 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = createLoanProduct(null);
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "10 January 2013");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "1 March 2013");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
         LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
 
-        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(AccountTransferTest.LOAN_APPROVAL_DATE, 
loanID);
+        loanStatusHashMap = 
this.loanTransactionHelper.approveLoan(LOAN_APPROVAL_DATE, loanID);
         LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
 
         String loanDetails = 
this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
-        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(AccountTransferTest.LOAN_APPROVAL_DATE_PLUS_ONE,
-                loanID, 
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(LOAN_APPROVAL_DATE_PLUS_ONE,
 loanID,
+                
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
 
         final Boolean isNPABefore = (Boolean) 
this.loanTransactionHelper.getLoanDetail(requestSpec, responseSpec, loanID, 
"isNPA");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/cob/CobPartitioningTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/cob/CobPartitioningTest.java
index 79089f6e8..084c9e4b3 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/cob/CobPartitioningTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/cob/CobPartitioningTest.java
@@ -128,26 +128,35 @@ public class CobPartitioningTest {
             // Let's create 1, 2, ..., N-1, N loans
             final CountDownLatch createLatch = new CountDownLatch(N);
             Integer loanProductID = createLoanProduct();
+            List<Future<?>> futures = new ArrayList<>();
             for (int i = 0; i < N; i++) {
-                Future<?> unused = executorService.submit(() -> {
+                futures.add(executorService.submit(() -> {
                     Integer clientID = createClient();
                     Integer loanID = createLoanForClient(clientID, 
loanProductID);
                     loanIds.add(loanID);
                     createLatch.countDown();
-                });
+                }));
             }
-            createLatch.await();
+            waitForFutures(futures, createLatch);
+            futures.clear();
 
             // Force close loans 3, 4, ... , N-3, N-2
             Collections.sort(loanIds);
             final CountDownLatch closeLatch = new CountDownLatch(N - 4);
             for (int i = 2; i < N - 2; i++) {
                 final int idx = i;
-                Future<?> unused = executorService.submit(() -> {
+                futures.add(executorService.submit(() -> {
                     LOAN_TRANSACTION_HELPER.forecloseLoan("02 March 2020", 
loanIds.get(idx));
                     closeLatch.countDown();
-                });
+                }));
             }
+            futures.forEach(future -> {
+                try {
+                    future.get(); // turn any possible async failures into 
errors
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            });
             closeLatch.await();
 
             // Let's retrieve the partitions
@@ -171,6 +180,17 @@ public class CobPartitioningTest {
         }
     }
 
+    private static void waitForFutures(List<Future<?>> futures, CountDownLatch 
createLatch) throws InterruptedException {
+        futures.forEach(future -> {
+            try {
+                future.get(); // turn any possible async failures into errors
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+        createLatch.await();
+    }
+
     private void setInitialBusinessDate(String date) {
         GlobalConfigurationHelper.updateIsBusinessDateEnabled(REQUEST_SPEC, 
RESPONSE_SPEC, Boolean.TRUE);
         BusinessDateHelper.updateBusinessDate(REQUEST_SPEC, RESPONSE_SPEC, 
BUSINESS_DATE, LocalDate.parse(date));
@@ -210,7 +230,7 @@ public class CobPartitioningTest {
 
         HashMap loanStatusHashMap;
 
-        Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), "10 January 2020");
+        Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), "1 March 2020");
 
         Assertions.assertNotNull(loanID);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
index 80b50eaec..6be2a4871 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
@@ -680,7 +680,7 @@ public final class BatchHelper {
         br.setMethod("POST");
         String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM 
yyyy"));
         br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", 
\"approvedOnDate\": \"" + dateString + "\","
-                + "\"note\": \"Loan approval note\"}");
+                + "\"note\": \"Loan approval note\", 
\"expectedDisbursementDate\": \"" + dateString + "\"}");
 
         return br;
     }
@@ -756,7 +756,7 @@ public final class BatchHelper {
      *            the action to transistion
      * @return BatchRequest the batch request
      */
-    public static BatchRequest transistionLoanStateByExternalId(final Long 
requestId, final Long reference, final LocalDate date,
+    public static BatchRequest transitionLoanStateByExternalId(final Long 
requestId, final Long reference, final LocalDate date,
             final String command) {
         final BatchRequest br = new BatchRequest();
 
@@ -769,7 +769,7 @@ public final class BatchHelper {
             br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", 
\"actualDisbursementDate\": \"" + dateString + "\"}");
         } else if ("approve".equals(command)) {
             br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", 
\"approvedOnDate\": \"" + dateString + "\","
-                    + "\"note\": \"Loan approval note\"}");
+                    + "\"note\": \"Loan approval note\", 
\"expectedDisbursementDate\": \"" + dateString + "\"}");
         }
 
         return br;
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
index 888bbd78e..91e820f8e 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
@@ -105,7 +105,7 @@ public class InlineLoanCOBTest {
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
 
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -190,7 +190,7 @@ public class InlineLoanCOBTest {
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
 
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -260,7 +260,7 @@ public class InlineLoanCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -318,7 +318,7 @@ public class InlineLoanCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -375,7 +375,7 @@ public class InlineLoanCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
@@ -442,7 +442,7 @@ public class InlineLoanCOBTest {
             final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
             Assertions.assertNotNull(loanProductID);
             HashMap loanStatusHashMap;
-            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
+            final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "1 
March 2020");
 
             Assertions.assertNotNull(loanID);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerTransferCancelTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerTransferCancelTest.java
index 7617989ca..de22d71bb 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerTransferCancelTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/ExternalAssetOwnerTransferCancelTest.java
@@ -307,7 +307,7 @@ public class ExternalAssetOwnerTransferCancelTest {
         Assertions.assertNotNull(loanProductID);
         HashMap loanStatusHashMap;
 
-        Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), "10 January 2020");
+        Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), "1 March 2020");
 
         Assertions.assertNotNull(loanID);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
index 752217f4b..dcab08598 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
@@ -1012,7 +1012,7 @@ public class InitiateExternalAssetOwnerTransferTest {
         Assertions.assertNotNull(loanProductID);
         HashMap loanStatusHashMap;
 
-        Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), "10 January 2020");
+        Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), "1 March 2020");
 
         Assertions.assertNotNull(loanID);
 

Reply via email to