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 3c6fc1e47 FINERACT-1958-Loan-Product-repayment-start-date-configuration
3c6fc1e47 is described below

commit 3c6fc1e47b981e98e3357fbfe00b25a7abb58a83
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Mon Aug 21 19:13:09 2023 +0530

    FINERACT-1958-Loan-Product-repayment-start-date-configuration
---
 .../portfolio/loanaccount/domain/Loan.java         |   7 +-
 .../loanschedule/domain/LoanApplicationTerms.java  |  27 +-
 .../loanproduct/LoanProductConstants.java          |   1 +
 .../portfolio/loanproduct/domain/LoanProduct.java  |  21 +-
 .../loanproduct/domain/RepaymentStartDateType.java |  62 +++
 .../loanproduct/service/LoanEnumerations.java      |  18 +
 .../tenant/module/loan/module-changelog-master.xml |   1 +
 ...product_repayment_start_date_configuration.xml} |  16 +-
 .../domain/AbstractLoanScheduleGenerator.java      |  26 +-
 .../domain/DefaultScheduledDateGenerator.java      |   9 +-
 .../service/LoanScheduleAssembler.java             |  60 ++-
 .../loanproduct/api/LoanProductsApiResource.java   |   8 +-
 .../api/LoanProductsApiResourceSwagger.java        |  31 ++
 .../loanproduct/data/LoanProductData.java          |  23 +-
 .../serialization/LoanProductDataValidator.java    |  18 +-
 .../service/LoanDropdownReadPlatformService.java   |   1 +
 .../LoanDropdownReadPlatformServiceImpl.java       |   8 +
 .../LoanProductReadPlatformServiceImpl.java        |   6 +-
 ...ProductRepaymentStartDateConfigurationTest.java | 474 +++++++++++++++++++++
 .../common/loans/LoanProductTestBuilder.java       |  10 +
 20 files changed, 771 insertions(+), 56 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index ed50efdde..90b52b80d 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
@@ -139,6 +139,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
 import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
 import org.apache.fineract.portfolio.rate.domain.Rate;
@@ -5853,6 +5854,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         RecalculationFrequencyType compoundingFrequencyType = null;
         LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
         CalendarHistoryDataWrapper calendarHistoryDataWrapper;
+        RepaymentStartDateType repaymentStartDateType = 
this.getLoanProduct().getRepaymentStartDateType();
         boolean allowCompoundingOnEod = false;
         if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
             restCalendarInstance = 
scheduleGeneratorDTO.getCalendarInstanceForInterestRecalculation();
@@ -5886,7 +5888,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                 calendarHistoryDataWrapper, 
scheduleGeneratorDTO.getNumberOfdays(), 
scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
                 holidayDetailDTO, allowCompoundingOnEod, 
scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
                 
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), 
this.fixedPrincipalPercentagePerInstallment,
-                
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans());
+                
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), 
repaymentStartDateType, getSubmittedOnDate());
     }
 
     public BigDecimal constructLoanTermVariations(FloatingRateDTO 
floatingRateDTO, BigDecimal annualNominalInterestRate,
@@ -6153,6 +6155,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         InterestRecalculationCompoundingMethod compoundingMethod = null;
         RecalculationFrequencyType compoundingFrequencyType = null;
         LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
+        RepaymentStartDateType repaymentStartDateType = 
loanProduct.getRepaymentStartDateType();
         boolean allowCompoundingOnEod = false;
         if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
             recalculationFrequencyType = 
this.loanInterestRecalculationDetails.getRestFrequencyType();
@@ -6173,7 +6176,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                 compoundingCalendarInstance, compoundingFrequencyType, 
this.loanProduct.preCloseInterestCalculationStrategy(),
                 rescheduleStrategyMethod, loanCalendar, 
getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
                 calendarHistoryDataWrapper, numberofdays, 
isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
-                false, this.fixedPrincipalPercentagePerInstallment, false);
+                false, this.fixedPrincipalPercentagePerInstallment, false, 
repaymentStartDateType, getSubmittedOnDate());
     }
 
     /**
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index 6d7ffe7d9..5c0e6d141 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -51,6 +51,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCa
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 
 @Slf4j
 public final class LoanApplicationTerms {
@@ -211,6 +212,9 @@ public final class LoanApplicationTerms {
     private BigDecimal disbursedAmountPercentageForDownPayment;
     private boolean isAutoRepaymentForDownPaymentEnabled;
 
+    private RepaymentStartDateType repaymentStartDateType;
+    private LocalDate submittedOnDate;
+
     public static LoanApplicationTerms assembleFrom(final ApplicationCurrency 
currency, final Integer loanTermFrequency,
             final PeriodFrequencyType loanTermPeriodFrequencyType, final 
Integer numberOfRepayments, final Integer repaymentEvery,
             final PeriodFrequencyType repaymentPeriodFrequencyType, Integer 
nthDay, DayOfWeekType weekDayType,
@@ -235,7 +239,8 @@ public final class LoanApplicationTerms {
             final boolean isEqualAmortization, final boolean 
isInterestToBeRecoveredFirstWhenGreaterThanEMI,
             final BigDecimal fixedPrincipalPercentagePerInstallment, final 
boolean isPrincipalCompoundingDisabledForOverdueLoans,
             final Boolean enableDownPayment, final BigDecimal 
disbursedAmountPercentageForDownPayment,
-            final Boolean isAutoRepaymentForDownPaymentEnabled) {
+            final Boolean isAutoRepaymentForDownPaymentEnabled, final 
RepaymentStartDateType repaymentStartDateType,
+            final LocalDate submittedOnDate) {
 
         final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
         final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
@@ -253,7 +258,7 @@ public final class LoanApplicationTerms {
                 isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, 
allowCompoundingOnEod, isEqualAmortization, false,
                 isInterestToBeRecoveredFirstWhenGreaterThanEMI, 
fixedPrincipalPercentagePerInstallment,
                 isPrincipalCompoundingDisabledForOverdueLoans, 
enableDownPayment, disbursedAmountPercentageForDownPayment,
-                isAutoRepaymentForDownPaymentEnabled);
+                isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, 
submittedOnDate);
 
     }
 
@@ -273,7 +278,8 @@ public final class LoanApplicationTerms {
             final CalendarHistoryDataWrapper calendarHistoryDataWrapper, final 
Integer numberOfDays,
             final boolean isSkipRepaymentOnFirstDayOfMonth, final 
HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
             final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean 
isInterestToBeRecoveredFirstWhenGreaterThanEMI,
-            final BigDecimal fixedPrincipalPercentagePerInstallment, final 
boolean isPrincipalCompoundingDisabledForOverdueLoans) {
+            final BigDecimal fixedPrincipalPercentagePerInstallment, final 
boolean isPrincipalCompoundingDisabledForOverdueLoans,
+            final RepaymentStartDateType repaymentStartDateType, final 
LocalDate submittedOnDate) {
 
         final Integer numberOfRepayments = 
loanProductRelatedDetail.getNumberOfRepayments();
         final Integer repaymentEvery = 
loanProductRelatedDetail.getRepayEvery();
@@ -321,7 +327,7 @@ public final class LoanApplicationTerms {
                 allowCompoundingOnEod, isEqualAmortization, 
isFirstRepaymentDateAllowedOnHoliday,
                 isInterestToBeRecoveredFirstWhenGreaterThanEMI, 
fixedPrincipalPercentagePerInstallment,
                 isPrincipalCompoundingDisabledForOverdueLoans, 
isDownPaymentEnabled, disbursedAmountPercentageForDownPayment,
-                isAutoRepaymentForDownPaymentEnabled);
+                isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, 
submittedOnDate);
     }
 
     private LoanApplicationTerms(final ApplicationCurrency currency, final 
Integer loanTermFrequency,
@@ -348,7 +354,8 @@ public final class LoanApplicationTerms {
             final boolean allowCompoundingOnEod, final boolean 
isEqualAmortization, final boolean isFirstRepaymentDateAllowedOnHoliday,
             final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
             final boolean isPrincipalCompoundingDisabledForOverdueLoans, final 
boolean isDownPaymentEnabled,
-            final BigDecimal disbursedAmountPercentageForDownPayment, final 
boolean isAutoRepaymentForDownPaymentEnabled) {
+            final BigDecimal disbursedAmountPercentageForDownPayment, final 
boolean isAutoRepaymentForDownPaymentEnabled,
+            final RepaymentStartDateType repaymentStartDateType, final 
LocalDate submittedOnDate) {
 
         this.currency = currency;
         this.loanTermFrequency = loanTermFrequency;
@@ -428,6 +435,8 @@ public final class LoanApplicationTerms {
         this.isDownPaymentEnabled = isDownPaymentEnabled;
         this.disbursedAmountPercentageForDownPayment = 
disbursedAmountPercentageForDownPayment;
         this.isAutoRepaymentForDownPaymentEnabled = 
isAutoRepaymentForDownPaymentEnabled;
+        this.repaymentStartDateType = repaymentStartDateType;
+        this.submittedOnDate = submittedOnDate;
     }
 
     public Money adjustPrincipalIfLastRepaymentPeriod(final Money 
principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -1745,4 +1754,12 @@ public final class LoanApplicationTerms {
     public BigDecimal getDisbursedAmountPercentageForDownPayment() {
         return disbursedAmountPercentageForDownPayment;
     }
+
+    public RepaymentStartDateType getRepaymentStartDateType() {
+        return repaymentStartDateType;
+    }
+
+    public LocalDate getSubmittedOnDate() {
+        return submittedOnDate;
+    }
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
index 0d6176b3d..3a3b75523 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
@@ -151,5 +151,6 @@ public interface LoanProductConstants {
     String ENABLE_DOWN_PAYMENT = "enableDownPayment";
     String DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT = 
"disbursedAmountPercentageForDownPayment";
     String ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT = 
"enableAutoRepaymentForDownPayment";
+    String REPAYMENT_START_DATE_TYPE = "repaymentStartDateType";
 
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index e74d9fcb0..89a3fe47a 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -213,6 +213,9 @@ public class LoanProduct extends AbstractPersistableCustom {
     @Column(name = "overdue_days_for_repayment_event")
     private Integer overDueDaysForRepaymentEvent;
 
+    @Column(name = "repayment_start_date_type_enum", nullable = false)
+    private RepaymentStartDateType repaymentStartDateType;
+
     public static LoanProduct assembleFromJson(final Fund fund, final String 
loanTransactionProcessingStrategy,
             final List<Charge> productCharges, final JsonCommand command, 
final AprCalculator aprCalculator, FloatingRate floatingRate,
             final List<Rate> productRates, 
List<LoanProductPaymentAllocationRule> loanProductPaymentAllocationRules) {
@@ -394,6 +397,9 @@ public class LoanProduct extends AbstractPersistableCustom {
         final boolean enableAutoRepaymentForDownPayment = command
                 
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT);
 
+        final RepaymentStartDateType repaymentStartDateType = 
RepaymentStartDateType
+                
.fromInt(command.integerValueOfParameterNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE));
+
         return new LoanProduct(fund, loanTransactionProcessingStrategy, 
loanProductPaymentAllocationRules, name, shortName, description,
                 currency, principal, minPrincipal, maxPrincipal, 
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod,
                 interestFrequencyType, annualInterestRate, interestMethod, 
interestCalculationPeriodMethod,
@@ -411,7 +417,7 @@ public class LoanProduct extends AbstractPersistableCustom {
                 syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment,
                 disallowExpectedDisbursements, 
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, 
overAppliedNumber,
                 dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, 
enableDownPayment, disbursedAmountPercentageDownPayment,
-                enableAutoRepaymentForDownPayment);
+                enableAutoRepaymentForDownPayment, repaymentStartDateType);
 
     }
 
@@ -625,7 +631,7 @@ public class LoanProduct extends AbstractPersistableCustom {
             final boolean allowApprovedDisbursedAmountsOverApplied, final 
String overAppliedCalculationType,
             final Integer overAppliedNumber, final Integer 
dueDaysForRepaymentEvent, final Integer overDueDaysForRepaymentEvent,
             final boolean enableDownPayment, final BigDecimal 
disbursedAmountPercentageForDownPayment,
-            final boolean enableAutoRepaymentForDownPayment) {
+            final boolean enableAutoRepaymentForDownPayment, final 
RepaymentStartDateType repaymentStartDateType) {
         this.fund = fund;
         this.transactionProcessingStrategyCode = 
transactionProcessingStrategyCode;
 
@@ -719,6 +725,7 @@ public class LoanProduct extends AbstractPersistableCustom {
 
         this.dueDaysForRepaymentEvent = dueDaysForRepaymentEvent;
         this.overDueDaysForRepaymentEvent = overDueDaysForRepaymentEvent;
+        this.repaymentStartDateType = repaymentStartDateType;
 
         validateLoanProductPreSave();
     }
@@ -1281,6 +1288,12 @@ public class LoanProduct extends 
AbstractPersistableCustom {
             
this.loanProductRelatedDetail.updateEnableAutoRepaymentForDownPayment(newValue);
         }
 
+        if 
(command.isChangeInIntegerParameterNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE,
+                this.repaymentStartDateType.getValue())) {
+            final Integer newValue = 
command.integerValueOfParameterNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE);
+            actualChanges.put(LoanProductConstants.REPAYMENT_START_DATE_TYPE, 
newValue);
+            this.repaymentStartDateType = 
RepaymentStartDateType.fromInt(newValue);
+        }
         return actualChanges;
     }
 
@@ -1676,4 +1689,8 @@ public class LoanProduct extends 
AbstractPersistableCustom {
         return this.overDueDaysForRepaymentEvent;
     }
 
+    public RepaymentStartDateType getRepaymentStartDateType() {
+        return this.repaymentStartDateType == null ? 
RepaymentStartDateType.INVALID : this.repaymentStartDateType;
+    }
+
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/RepaymentStartDateType.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/RepaymentStartDateType.java
new file mode 100644
index 000000000..7d4e88fbc
--- /dev/null
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/RepaymentStartDateType.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanproduct.domain;
+
+public enum RepaymentStartDateType {
+
+    INVALID(0, "repaymentStartDateType.invalid"), //
+    DISBURSEMENT_DATE(1, "repaymentStartDateType.disbursementDate"), //
+    SUBMITTED_ON_DATE(2, "repaymentStartDateType.submittedOnDate");
+
+    private final Integer value;
+    private final String code;
+
+    RepaymentStartDateType(Integer value, String code) {
+        this.value = value;
+        this.code = code;
+    }
+
+    public static RepaymentStartDateType fromInt(final Integer 
repaymentStartDateType) {
+        if (repaymentStartDateType == null) {
+            return RepaymentStartDateType.DISBURSEMENT_DATE;
+        }
+        return switch (repaymentStartDateType) {
+            case 1 -> RepaymentStartDateType.DISBURSEMENT_DATE;
+            case 2 -> RepaymentStartDateType.SUBMITTED_ON_DATE;
+            default -> RepaymentStartDateType.INVALID;
+        };
+    }
+
+    public Integer getValue() {
+        return this.value;
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public boolean isDisbursementDate() {
+        return RepaymentStartDateType.DISBURSEMENT_DATE.equals(this);
+    }
+
+    public boolean isSubmittedOnDate() {
+        return RepaymentStartDateType.SUBMITTED_ON_DATE.equals(this);
+    }
+
+}
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
index 735caf788..affa36385 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
@@ -38,6 +38,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductParamType;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 
 public final class LoanEnumerations {
 
@@ -57,6 +58,7 @@ public final class LoanEnumerations {
     public static final String LOAN_TYPE = "loanType";
     public static final String INTEREST_RECALCULATION_COMPOUNDING_TYPE = 
"interestRecalculationCompoundingType";
     public static final String RESCHEDULE_STRATEGY_TYPE = 
"rescheduleStrategyType";
+    public static final String REPAYMENT_START_DATE_TYPE = 
"repaymentStartDateType";
 
     public static EnumOptionData loanEnumeration(final String typeName, final 
int id) {
         return switch (typeName) {
@@ -71,10 +73,26 @@ public final class LoanEnumerations {
             case LOAN_TYPE -> AccountEnumerations.loanType(id);
             case INTEREST_RECALCULATION_COMPOUNDING_TYPE -> 
interestRecalculationCompoundingType(id);
             case RESCHEDULE_STRATEGY_TYPE -> rescheduleStrategyType(id);
+            case REPAYMENT_START_DATE_TYPE -> repaymentStartDateType(id);
             default -> null;
         };
     }
 
+    public static EnumOptionData repaymentStartDateType(int id) {
+        return repaymentStartDateType(RepaymentStartDateType.fromInt(id));
+    }
+
+    public static EnumOptionData repaymentStartDateType(final 
RepaymentStartDateType type) {
+        return switch (type) {
+            case DISBURSEMENT_DATE -> new 
EnumOptionData(RepaymentStartDateType.DISBURSEMENT_DATE.getValue().longValue(),
+                    RepaymentStartDateType.DISBURSEMENT_DATE.getCode(), 
"Disbursement Date");
+            case SUBMITTED_ON_DATE -> new 
EnumOptionData(RepaymentStartDateType.SUBMITTED_ON_DATE.getValue().longValue(),
+                    RepaymentStartDateType.SUBMITTED_ON_DATE.getCode(), 
"Submitted On Date");
+            default -> new 
EnumOptionData(RepaymentStartDateType.INVALID.getValue().longValue(), 
RepaymentStartDateType.INVALID.getCode(),
+                    "Invalid");
+        };
+    }
+
     public static EnumOptionData loanTermFrequencyType(final int id) {
         return loanTermFrequencyType(PeriodFrequencyType.fromInt(id));
     }
diff --git 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
index f9e4a3859..0f352e5d6 100644
--- 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
+++ 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
@@ -27,4 +27,5 @@
   <include relativeToChangelogFile="true" 
file="parts/1002_add_payment_allocation_rule.xml"/>
   <include relativeToChangelogFile="true" 
file="parts/1003_add_loan_product_auto_repayment_down_payment_configuration.xml"/>
   <include relativeToChangelogFile="true" 
file="parts/1004_add_external_event_configuration_for_down_payment_transaction_event.xml"/>
+  <include relativeToChangelogFile="true" 
file="parts/1005_add_loan_product_repayment_start_date_configuration.xml"/>
 </databaseChangeLog>
diff --git 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1005_add_loan_product_repayment_start_date_configuration.xml
similarity index 56%
copy from 
fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
copy to 
fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1005_add_loan_product_repayment_start_date_configuration.xml
index f9e4a3859..b0829998d 100644
--- 
a/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/module-changelog-master.xml
+++ 
b/fineract-loan/src/main/resources/db/changelog/tenant/module/loan/parts/1005_add_loan_product_repayment_start_date_configuration.xml
@@ -20,11 +20,13 @@
 
 -->
 <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog";
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
-  <!-- Sequence is starting from 1000 to make it easier to move existing 
liquibase changesets here -->
-  <include relativeToChangelogFile="true" 
file="parts/1001_add_audit_fields_to_loan_charge.xml"/>
-  <include relativeToChangelogFile="true" 
file="parts/1002_add_payment_allocation_rule.xml"/>
-  <include relativeToChangelogFile="true" 
file="parts/1003_add_loan_product_auto_repayment_down_payment_configuration.xml"/>
-  <include relativeToChangelogFile="true" 
file="parts/1004_add_external_event_configuration_for_down_payment_transaction_event.xml"/>
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+                   
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd";>
+    <changeSet author="fineract" id="1">
+        <addColumn tableName="m_product_loan">
+            <column defaultValueNumeric="1" 
name="repayment_start_date_type_enum" type="SMALLINT">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+    </changeSet>
 </databaseChangeLog>
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index d7063ad2c..e152c78da 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -57,6 +57,7 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.exception.ScheduleDateException;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 
 public abstract class AbstractLoanScheduleGenerator implements 
LoanScheduleGenerator {
 
@@ -91,14 +92,16 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
 
         final MonetaryCurrency currency = loanApplicationTerms.getCurrency();
         final int numberOfRepayments = 
loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions();
-        LoanScheduleParams scheduleParams;
+        LoanScheduleParams scheduleParams = null;
+        LocalDate periodStartDate = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(loanApplicationTerms.getRepaymentStartDateType())
+                ? loanApplicationTerms.getExpectedDisbursementDate()
+                : loanApplicationTerms.getSubmittedOnDate();
         if (loanScheduleParams == null) {
             scheduleParams = 
LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, 
chargesDueAtTimeOfDisbursement),
-                    loanApplicationTerms.getExpectedDisbursementDate(), 
getPrincipalToBeScheduled(loanApplicationTerms));
+                    periodStartDate, 
getPrincipalToBeScheduled(loanApplicationTerms));
         } else if (!loanScheduleParams.isPartialUpdate()) {
             scheduleParams = 
LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, 
chargesDueAtTimeOfDisbursement),
-                    loanApplicationTerms.getExpectedDisbursementDate(), 
getPrincipalToBeScheduled(loanApplicationTerms),
-                    loanScheduleParams);
+                    periodStartDate, 
getPrincipalToBeScheduled(loanApplicationTerms), loanScheduleParams);
         } else {
             scheduleParams = loanScheduleParams;
         }
@@ -124,8 +127,11 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         }
 
         boolean isFirstRepayment = true;
-        LocalDate firstRepaymentdate = this.scheduledDateGenerator
-                
.generateNextRepaymentDate(loanApplicationTerms.getExpectedDisbursementDate(), 
loanApplicationTerms, isFirstRepayment);
+        LocalDate lastRepaymentDate = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(loanApplicationTerms.getRepaymentStartDateType())
+                ? loanApplicationTerms.getExpectedDisbursementDate()
+                : loanApplicationTerms.getSubmittedOnDate();
+        LocalDate firstRepaymentdate = 
this.scheduledDateGenerator.generateNextRepaymentDate(lastRepaymentDate, 
loanApplicationTerms,
+                isFirstRepayment);
         final LocalDate idealDisbursementDate = 
this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
                 loanApplicationTerms.getLoanTermPeriodFrequencyType(), 
loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate,
                 loanApplicationTerms.getLoanCalendar(), 
loanApplicationTerms.getHolidayDetailDTO(), loanApplicationTerms);
@@ -2207,7 +2213,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             if (loanApplicationTerms.isInterestRecalculationEnabled()) {
                 lastRestDate = 
getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, 
holidayDetailDTO);
             }
-            LocalDate actualRepaymentDate = 
loanApplicationTerms.getExpectedDisbursementDate();
+            LocalDate actualRepaymentDate = 
RepaymentStartDateType.DISBURSEMENT_DATE
+                    .equals(loanApplicationTerms.getRepaymentStartDateType()) 
? loanApplicationTerms.getExpectedDisbursementDate()
+                            : loanApplicationTerms.getSubmittedOnDate();
             boolean isFirstRepayment = true;
 
             // cumulative fields
@@ -2222,7 +2230,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             // Actual period Number plus interest only repayments
             int instalmentNumber = 1;
             LocalDate lastInstallmentDate = actualRepaymentDate;
-            LocalDate periodStartDate = 
loanApplicationTerms.getExpectedDisbursementDate();
+            LocalDate periodStartDate = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(loanApplicationTerms.getRepaymentStartDateType())
+                    ? loanApplicationTerms.getExpectedDisbursementDate()
+                    : loanApplicationTerms.getSubmittedOnDate();
             // Set fixed Amortization Amounts(either EMI or Principal )
             updateAmortization(mc, loanApplicationTerms, periodNumber, 
outstandingBalance);
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
index 04cc076c3..804cb1c40 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
@@ -32,6 +32,7 @@ import 
org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
 import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
 import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
 import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,7 +45,11 @@ public class DefaultScheduledDateGenerator implements 
ScheduledDateGenerator {
 
         final int numberOfRepayments = 
loanApplicationTerms.getNumberOfRepayments();
 
-        LocalDate lastRepaymentDate = 
loanApplicationTerms.getExpectedDisbursementDate();
+        RepaymentStartDateType repaymentStartDateType = 
loanApplicationTerms.getRepaymentStartDateType();
+
+        LocalDate lastRepaymentDate = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)
+                ? loanApplicationTerms.getExpectedDisbursementDate()
+                : loanApplicationTerms.getSubmittedOnDate();
         boolean isFirstRepayment = true;
         for (int repaymentPeriod = 1; repaymentPeriod <= numberOfRepayments; 
repaymentPeriod++) {
             lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, 
loanApplicationTerms, isFirstRepayment);
@@ -125,8 +130,6 @@ public class DefaultScheduledDateGenerator implements 
ScheduledDateGenerator {
      * @param adjustedDateDetailsDTO
      * @param loanApplicationTerms
      * @param holidayDetailDTO
-     * @param nextRepaymentPeriodDueDate
-     * @param rescheduleType
      * @param isFirstRepayment
      * @return
      */
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 55384b5b7..9b7d5e077 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
@@ -112,6 +112,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductVariableInstallmentConfig;
 import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
+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.springframework.beans.factory.annotation.Autowired;
@@ -234,7 +235,11 @@ public class LoanScheduleAssembler {
         final Money principalMoney = Money.of(currency, principal);
 
         final LocalDate expectedDisbursementDate = 
this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", 
element);
-        final LocalDate repaymentsStartingFromDate = 
this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", 
element);
+        LocalDate repaymentsStartingFromDate = 
this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", 
element);
+        final LocalDate submittedOnDate = 
this.fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
+
+        final RepaymentStartDateType repaymentStartDateType = 
loanProduct.getRepaymentStartDateType();
+
         LocalDate calculatedRepaymentsStartingFromDate = 
repaymentsStartingFromDate;
 
         final Boolean synchDisbursement = 
this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", 
element);
@@ -261,7 +266,8 @@ public class LoanScheduleAssembler {
                     && !nthDay.equals(NthDayType.INVALID.getValue())) {
                 LocalDate calendarStartDate = repaymentsStartingFromDate;
                 if (calendarStartDate == null) {
-                    calendarStartDate = expectedDisbursementDate;
+                    calendarStartDate = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType) ? 
expectedDisbursementDate
+                            : submittedOnDate;
                 }
                 calendar = createLoanCalendar(calendarStartDate, 
repaymentEvery, CalendarFrequencyType.MONTHLY, dayOfWeek, nthDay);
             }
@@ -272,7 +278,8 @@ public class LoanScheduleAssembler {
          */
         if (calculatedRepaymentsStartingFromDate == null) {
             calculatedRepaymentsStartingFromDate = 
deriveFirstRepaymentDate(loanType, repaymentEvery, expectedDisbursementDate,
-                    repaymentPeriodFrequencyType, 
loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment(), calendar);
+                    repaymentPeriodFrequencyType, 
loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment(), calendar, 
submittedOnDate,
+                    repaymentStartDateType);
         }
 
         /*
@@ -305,8 +312,10 @@ public class LoanScheduleAssembler {
             }
         }
 
-        
validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, 
calculatedRepaymentsStartingFromDate,
-                loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment());
+        if 
(RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)) {
+            
validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, 
calculatedRepaymentsStartingFromDate,
+                    
loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment());
+        }
 
         // grace details
         final Integer graceOnPrincipalPayment = 
this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", 
element);
@@ -471,7 +480,7 @@ public class LoanScheduleAssembler {
                 loanTermVariations, 
isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays, 
isSkipMeetingOnFirstDay, detailDTO,
                 allowCompoundingOnEod, isEqualAmortization, 
isInterestToBeRecoveredFirstWhenGreaterThanEMI,
                 fixedPrincipalPercentagePerInstallment, 
isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
-                disbursedAmountPercentageForDownPayment, 
isAutoRepaymentForDownPaymentEnabled);
+                disbursedAmountPercentageForDownPayment, 
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate);
     }
 
     private CalendarInstance createCalendarForSameAsRepayment(final Integer 
repaymentEvery,
@@ -1070,19 +1079,20 @@ public class LoanScheduleAssembler {
 
     private LocalDate deriveFirstRepaymentDate(final AccountType loanType, 
final Integer repaymentEvery,
             final LocalDate expectedDisbursementDate, final 
PeriodFrequencyType repaymentPeriodFrequencyType,
-            final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final 
Calendar calendar) {
+            final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final 
Calendar calendar, final LocalDate submittedOnDate,
+            final RepaymentStartDateType repaymentStartDateType) {
 
         LocalDate derivedFirstRepayment = null;
 
-        final LocalDate 
dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = 
expectedDisbursementDate
-                .plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
+        final LocalDate 
dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(
+                repaymentStartDateType) ? 
expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment) 
: submittedOnDate;
 
         if (calendar != null) {
 
             final LocalDate refernceDateForCalculatingFirstRepaymentDate = 
expectedDisbursementDate;
             derivedFirstRepayment = 
deriveFirstRepaymentDateForLoans(repaymentEvery, expectedDisbursementDate,
                     refernceDateForCalculatingFirstRepaymentDate, 
repaymentPeriodFrequencyType,
-                    minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
+                    minimumDaysBetweenDisbursalAndFirstRepayment, calendar, 
submittedOnDate, repaymentStartDateType);
 
         } /*** Individual or group account, or JLG not linked to a meeting ***/
         else {
@@ -1092,14 +1102,26 @@ public class LoanScheduleAssembler {
             // (disbursement date + minimum between disbursal and first
             // repayment )
             if (repaymentPeriodFrequencyType.isDaily()) {
-                dateBasedOnRepaymentFrequency = 
expectedDisbursementDate.plusDays(repaymentEvery);
+                dateBasedOnRepaymentFrequency = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)
+                        ? expectedDisbursementDate.plusDays(repaymentEvery)
+                        : submittedOnDate.plusDays(repaymentEvery);
+
             } else if (repaymentPeriodFrequencyType.isWeekly()) {
-                dateBasedOnRepaymentFrequency = 
expectedDisbursementDate.plusWeeks(repaymentEvery);
+                dateBasedOnRepaymentFrequency = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)
+                        ? expectedDisbursementDate.plusWeeks(repaymentEvery)
+                        : submittedOnDate.plusWeeks(repaymentEvery);
+
             } else if (repaymentPeriodFrequencyType.isMonthly()) {
-                dateBasedOnRepaymentFrequency = 
expectedDisbursementDate.plusMonths(repaymentEvery);
+                dateBasedOnRepaymentFrequency = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)
+                        ? expectedDisbursementDate.plusMonths(repaymentEvery)
+                        : submittedOnDate.plusMonths(repaymentEvery);
+
             } /** yearly loan **/
             else {
-                dateBasedOnRepaymentFrequency = 
expectedDisbursementDate.plusYears(repaymentEvery);
+                dateBasedOnRepaymentFrequency = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)
+                        ? expectedDisbursementDate.plusYears(repaymentEvery)
+                        : submittedOnDate.plusYears(repaymentEvery);
+
             }
             derivedFirstRepayment = 
dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment)
                     ? dateBasedOnRepaymentFrequency
@@ -1111,16 +1133,20 @@ public class LoanScheduleAssembler {
 
     private LocalDate deriveFirstRepaymentDateForLoans(final Integer 
repaymentEvery, final LocalDate expectedDisbursementDate,
             final LocalDate refernceDateForCalculatingFirstRepaymentDate, 
final PeriodFrequencyType repaymentPeriodFrequencyType,
-            final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final 
Calendar calendar) {
+            final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final 
Calendar calendar, final LocalDate submittedOnDate,
+            final RepaymentStartDateType repaymentStartDateType) {
         boolean isMeetingSkipOnFirstDayOfMonth = 
configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
         int numberOfDays = 
configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue();
         final String frequency = 
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
         final LocalDate derivedFirstRepayment = 
CalendarUtils.getFirstRepaymentMeetingDate(calendar,
                 refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, 
frequency, isMeetingSkipOnFirstDayOfMonth, numberOfDays);
-        final LocalDate minimumFirstRepaymentDate = 
expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
+        final LocalDate minimumFirstRepaymentDate = 
RepaymentStartDateType.DISBURSEMENT_DATE.equals(repaymentStartDateType)
+                ? 
expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment)
+                : submittedOnDate;
         return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? 
derivedFirstRepayment
                 : deriveFirstRepaymentDateForLoans(repaymentEvery, 
expectedDisbursementDate, derivedFirstRepayment,
-                        repaymentPeriodFrequencyType, 
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
+                        repaymentPeriodFrequencyType, 
minimumDaysBetweenDisbursalAndFirstRepayment, calendar, submittedOnDate,
+                        repaymentStartDateType);
     }
 
     private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final 
LocalDate disbursalDate, final LocalDate firstRepaymentDate,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
index 7e427ff67..67fda06e3 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -112,7 +112,8 @@ public class LoanProductsApiResource {
             LoanProductConstants.CAN_USE_FOR_TOPUP, 
LoanProductConstants.IS_EQUAL_AMORTIZATION_PARAM, 
LoanProductConstants.RATES_PARAM_NAME,
             LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, 
LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT,
             LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, 
LoanProductConstants.ENABLE_DOWN_PAYMENT,
-            LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, 
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT));
+            LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, 
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT,
+            LoanProductConstants.REPAYMENT_START_DATE_TYPE));
 
     private static final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new 
HashSet<>(
             Arrays.asList("productId", "productName", "restrictedProducts", 
"allowedProducts", "productOptions"));
@@ -147,7 +148,7 @@ public class LoanProductsApiResource {
     @Operation(summary = "Create a Loan Product", description = "Depending of 
the Accounting Rule (accountingRule) selected, additional fields with details 
of the appropriate Ledger Account identifiers would need to be passed in.\n"
             + "\n" + "Refer MifosX Accounting Specs Draft for more details 
regarding the significance of the selected accounting rule\n\n"
             + "Mandatory Fields: name, shortName, currencyCode, 
digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, 
repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, 
interestRateFrequencyType, amortizationType, interestType, 
interestCalculationPeriodType, transactionProcessingStrategyCode, 
accountingRule, isInterestRecalculationEnabled, daysInYearType, 
daysInMonthType\n\n"
-            + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, 
graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, 
paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, 
penaltyToIncomeAccountMappings, includeInBorrowerCycle, 
useBorrowerCycle,principalVariationsForBorrowerCycle, 
numberOfRepaymentVariationsForBorrowerCycle, 
interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, 
outstandingLoanBalance,overdueDaysForNPA,h [...]
+            + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, 
graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, 
paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, 
penaltyToIncomeAccountMappings, includeInBorrowerCycle, 
useBorrowerCycle,principalVariationsForBorrowerCycle, 
numberOfRepaymentVariationsForBorrowerCycle, 
interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, 
outstandingLoanBalance,overdueDaysForNPA,h [...]
             + "Additional Mandatory Fields for Cash(2) based accounting: 
fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, 
incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, 
transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n"
             + "Additional Mandatory Fields for periodic (3) and upfront 
(4)accrual accounting: fundSourceAccountId, loanPortfolioAccountId, 
interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, 
writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId, 
receivablePenaltyAccountId, transfersInSuspenseAccountId, 
overpaymentLiabilityAccountId\n\n"
             + "Additional Mandatory Fields if interest recalculation is 
enabled(true): interestRecalculationCompoundingMethod, 
rescheduleStrategyMethod, recalculationRestFrequencyType\n\n"
@@ -405,6 +406,7 @@ public class LoanProductsApiResource {
         final List<EnumOptionData> preCloseInterestCalculationStrategyOptions 
= dropdownReadPlatformService
                 .retrievePreCloseInterestCalculationStrategyOptions();
         final List<FloatingRateData> floatingRateOptions = 
this.floatingRateReadPlatformService.retrieveLookupActive();
+        final List<EnumOptionData> repaymentStartDateTypeOptions = 
dropdownReadPlatformService.retrieveRepaymentStartDateTypeOptions();
 
         return new LoanProductData(productData, chargeOptions, penaltyOptions, 
paymentTypeOptions, currencyOptions, amortizationTypeOptions,
                 interestTypeOptions, interestCalculationPeriodTypeOptions, 
repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
@@ -412,7 +414,7 @@ public class LoanProductsApiResource {
                 loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, 
daysInYearTypeOptions,
                 interestRecalculationCompoundingTypeOptions, 
rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions,
                 preCloseInterestCalculationStrategyOptions, 
floatingRateOptions, interestRecalculationNthDayTypeOptions,
-                interestRecalculationDayOfWeekTypeOptions, isRatesEnabled, 
delinquencyBucketOptions);
+                interestRecalculationDayOfWeekTypeOptions, isRatesEnabled, 
delinquencyBucketOptions, repaymentStartDateTypeOptions);
     }
 
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 42b284e07..2b586572f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -162,6 +162,8 @@ final class LoanProductsApiResourceSwagger {
         public BigDecimal disbursedAmountPercentageForDownPayment;
         @Schema(example = "false")
         public Boolean enableAutoRepaymentForDownPayment;
+        @Schema(example = "1")
+        public Integer repaymentStartDateType;
 
         // Interest Recalculation
         @Schema(example = "false")
@@ -419,6 +421,18 @@ final class LoanProductsApiResourceSwagger {
             public String description;
         }
 
+        static final class GetLoanProductsRepaymentStartDateType {
+
+            private GetLoanProductsRepaymentStartDateType() {}
+
+            @Schema(example = "1")
+            public Long id;
+            @Schema(example = "repaymentStartDateType.disbursementDate")
+            public String code;
+            @Schema(example = "Disbursement Date")
+            public String description;
+        }
+
         static final class GetLoanProductsInterestRecalculationData {
 
             private GetLoanProductsInterestRecalculationData() {}
@@ -562,6 +576,7 @@ final class LoanProductsApiResourceSwagger {
         public GetLoanProductsResponse.GetLoanProductsAccountingRule 
accountingRule;
         @Schema(example = "0")
         public Integer principalThresholdForLastInstalment;
+        public GetLoanProductsResponse.GetLoanProductsRepaymentStartDateType 
repaymentStartDateType;
     }
 
     @Schema(description = "GetLoanProductsTemplateResponse")
@@ -1015,6 +1030,7 @@ final class LoanProductsApiResourceSwagger {
         public 
Set<GetLoanProductsResponse.GetLoanProductsInterestRecalculationData.GetLoanProductsInterestRecalculationCompoundingType>
 interestRecalculationCompoundingTypeOptions;
         public 
Set<GetLoanProductsResponse.GetLoanProductsInterestRecalculationData.GetLoanProductsRescheduleStrategyType>
 rescheduleStrategyTypeOptions;
         public 
Set<GetLoanProductsResponse.GetLoanProductsInterestRecalculationData.GetLoanProductsInterestRecalculationCompoundingFrequencyType>
 interestRecalculationFrequencyTypeOptions;
+        public 
Set<GetLoanProductsResponse.GetLoanProductsRepaymentStartDateType> 
repaymentStartDateTypeOptions;
     }
 
     @Schema(description = "GetLoanProductsProductIdResponse")
@@ -1034,6 +1050,18 @@ final class LoanProductsApiResourceSwagger {
             public String description;
         }
 
+        static final class GetLoanProductsRepaymentStartDateType {
+
+            private GetLoanProductsRepaymentStartDateType() {}
+
+            @Schema(example = "1")
+            public Long id;
+            @Schema(example = "repaymentStartDateType.disbursementDate")
+            public String code;
+            @Schema(example = "Disbursement Date")
+            public String description;
+        }
+
         static final class GetLoanProductsPrincipalVariationsForBorrowerCycle {
 
             private GetLoanProductsPrincipalVariationsForBorrowerCycle() {}
@@ -1218,6 +1246,7 @@ final class LoanProductsApiResourceSwagger {
         public BigDecimal disbursedAmountPercentageForDownPayment;
         @Schema(example = "false")
         public Boolean enableAutoRepaymentForDownPayment;
+        public GetLoanProductsRepaymentStartDateType repaymentStartDateType;
     }
 
     @Schema(description = "PutLoanProductsProductIdRequest")
@@ -1346,6 +1375,8 @@ final class LoanProductsApiResourceSwagger {
         public BigDecimal disbursedAmountPercentageForDownPayment;
         @Schema(example = "false")
         public Boolean enableAutoRepaymentForDownPayment;
+        @Schema(example = "1")
+        public Integer repaymentStartDateType;
 
         // Interest Recalculation
         @Schema(example = "false")
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index 06392b404..014ac6d75 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -48,6 +48,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
 import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import org.apache.fineract.portfolio.rate.data.RateData;
@@ -123,6 +124,7 @@ public class LoanProductData implements Serializable {
     private final Integer minimumDaysBetweenDisbursalAndFirstRepayment;
     private final boolean canDefineInstallmentAmount;
     private final Integer installmentAmountInMultiplesOf;
+    private final EnumOptionData repaymentStartDateType;
 
     // charges
     private final Collection<ChargeData> charges;
@@ -168,6 +170,7 @@ public class LoanProductData implements Serializable {
 
     private final List<EnumOptionData> 
interestRecalculationFrequencyTypeOptions;
     private final List<FloatingRateData> floatingRateOptions;
+    private final List<EnumOptionData> repaymentStartDateTypeOptions;
 
     private final Boolean multiDisburseLoan;
     private final Integer maxTrancheCount;
@@ -290,6 +293,7 @@ public class LoanProductData implements Serializable {
         final BigDecimal disbursedAmountPercentageDownPayment = null;
         final Collection<AdvancedPaymentData> paymentAllocation = null;
         final boolean enableAutoRepaymentForDownPayment = false;
+        final EnumOptionData repaymentStartDateType = null;
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -309,7 +313,7 @@ public class LoanProductData implements Serializable {
                 syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
                 fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
                 overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation);
+                paymentAllocation, repaymentStartDateType);
 
     }
 
@@ -403,6 +407,7 @@ public class LoanProductData implements Serializable {
         final BigDecimal disbursedAmountPercentageDownPayment = null;
         final boolean enableAutoRepaymentForDownPayment = false;
         final Collection<AdvancedPaymentData> paymentAllocation = null;
+        final EnumOptionData repaymentStartDateType = null;
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -422,7 +427,7 @@ public class LoanProductData implements Serializable {
                 syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
                 fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
                 overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation);
+                paymentAllocation, repaymentStartDateType);
 
     }
 
@@ -523,6 +528,7 @@ public class LoanProductData implements Serializable {
         final BigDecimal disbursedAmountPercentageDownPayment = null;
         final boolean enableAutoRepaymentForDownPayment = false;
         final Collection<AdvancedPaymentData> paymentAllocation = null;
+        final EnumOptionData repaymentStartDateType = 
LoanEnumerations.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE);
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -542,7 +548,7 @@ public class LoanProductData implements Serializable {
                 syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
                 fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
                 overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation);
+                paymentAllocation, repaymentStartDateType);
 
     }
 
@@ -637,6 +643,7 @@ public class LoanProductData implements Serializable {
         final BigDecimal disbursedAmountPercentageDownPayment = null;
         final boolean enableAutoRepaymentForDownPayment = false;
         final Collection<AdvancedPaymentData> paymentAllocation = null;
+        final EnumOptionData repaymentStartDateType = 
LoanEnumerations.repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE);
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -656,7 +663,7 @@ public class LoanProductData implements Serializable {
                 syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
                 fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
                 overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation);
+                paymentAllocation, repaymentStartDateType);
     }
 
     public static LoanProductData withAccountingDetails(final LoanProductData 
productData, final Map<String, Object> accountingMappings,
@@ -704,7 +711,7 @@ public class LoanProductData implements Serializable {
             final DelinquencyBucketData delinquencyBucket, final Integer 
dueDaysForRepaymentEvent,
             final Integer overDueDaysForRepaymentEvent, final boolean 
enableDownPayment,
             final BigDecimal disbursedAmountPercentageForDownPayment, final 
boolean enableAutoRepaymentForDownPayment,
-            final Collection<AdvancedPaymentData> paymentAllocation) {
+            final Collection<AdvancedPaymentData> paymentAllocation, final 
EnumOptionData repaymentStartDateType) {
         this.id = id;
         this.name = name;
         this.shortName = shortName;
@@ -826,6 +833,8 @@ public class LoanProductData implements Serializable {
         this.disbursedAmountPercentageForDownPayment = 
disbursedAmountPercentageForDownPayment;
         this.paymentAllocation = paymentAllocation;
         this.enableAutoRepaymentForDownPayment = 
enableAutoRepaymentForDownPayment;
+        this.repaymentStartDateType = repaymentStartDateType;
+        this.repaymentStartDateTypeOptions = null;
     }
 
     public LoanProductData(final LoanProductData productData, final 
Collection<ChargeData> chargeOptions,
@@ -842,7 +851,7 @@ public class LoanProductData implements Serializable {
             final List<EnumOptionData> 
preCloseInterestCalculationStrategyOptions, final List<FloatingRateData> 
floatingRateOptions,
             final List<EnumOptionData> interestRecalculationNthDayTypeOptions,
             final List<EnumOptionData> 
interestRecalculationDayOfWeekTypeOptions, final boolean isRatesEnabled,
-            final Collection<DelinquencyBucketData> delinquencyBucketOptions) {
+            final Collection<DelinquencyBucketData> delinquencyBucketOptions, 
final List<EnumOptionData> repaymentStartDateTypeOptions) {
         this.id = productData.id;
         this.name = productData.name;
         this.shortName = productData.shortName;
@@ -981,6 +990,8 @@ public class LoanProductData implements Serializable {
         this.disbursedAmountPercentageForDownPayment = 
productData.disbursedAmountPercentageForDownPayment;
         this.enableAutoRepaymentForDownPayment = 
productData.enableAutoRepaymentForDownPayment;
         this.paymentAllocation = productData.paymentAllocation;
+        this.repaymentStartDateType = productData.repaymentStartDateType;
+        this.repaymentStartDateTypeOptions = repaymentStartDateTypeOptions;
     }
 
     private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> 
charges) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index c6769617b..c130c51a8 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -162,7 +162,7 @@ public final class LoanProductDataValidator {
             LoanProductConstants.OVER_APPLIED_NUMBER, 
LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME,
             LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, 
LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT,
             LoanProductConstants.ENABLE_DOWN_PAYMENT, 
LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
-            LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT));
+            LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, 
LoanProductConstants.REPAYMENT_START_DATE_TYPE));
 
     private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { 
LoanProductConstants.amortizationTypeParamName,
             LoanProductConstants.interestTypeParamName, 
LoanProductConstants.transactionProcessingStrategyCodeParamName,
@@ -755,6 +755,13 @@ public final class LoanProductDataValidator {
             validateAutoRepaymentForDownPayment(enableDownPayment, 
baseDataValidator, element);
         }
 
+        if 
(this.fromApiJsonHelper.parameterExists(LoanProductConstants.REPAYMENT_START_DATE_TYPE,
 element)) {
+            final Integer repaymentStartDateType = this.fromApiJsonHelper
+                    
.extractIntegerNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE, element, 
Locale.getDefault());
+            
baseDataValidator.reset().parameter(LoanProductConstants.REPAYMENT_START_DATE_TYPE).value(repaymentStartDateType).notNull()
+                    .isOneOfTheseValues(1, 2);
+        }
+
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 
@@ -1679,6 +1686,15 @@ public final class LoanProductDataValidator {
             validateDownPaymentPercentage(enableDownPayment, 
baseDataValidator, element);
             validateAutoRepaymentForDownPayment(enableDownPayment, 
baseDataValidator, element);
         }
+
+        Integer repaymentStartDateType = 
loanProduct.getRepaymentStartDateType().getValue();
+        if 
(this.fromApiJsonHelper.parameterExists(LoanProductConstants.REPAYMENT_START_DATE_TYPE,
 element)) {
+            repaymentStartDateType = 
this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.REPAYMENT_START_DATE_TYPE,
 element,
+                    Locale.getDefault());
+        }
+        
baseDataValidator.reset().parameter(LoanProductConstants.REPAYMENT_START_DATE_TYPE).value(repaymentStartDateType).notNull()
+                .isOneOfTheseValues(1, 2);
+
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java
index c07784a97..91cda2f6e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java
@@ -57,4 +57,5 @@ public interface LoanDropdownReadPlatformService {
 
     List<EnumOptionData> retrievePreCloseInterestCalculationStrategyOptions();
 
+    List<EnumOptionData> retrieveRepaymentStartDateTypeOptions();
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java
index c9659445e..a1fa9f192 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java
@@ -32,6 +32,7 @@ import static 
org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations
 import static 
org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyDayOfWeekType;
 import static 
org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyNthDayType;
 import static 
org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
+import static 
org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.repaymentStartDateType;
 import static 
org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType;
 
 import java.util.Arrays;
@@ -52,6 +53,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCa
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductValueConditionType;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
+import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -177,4 +179,10 @@ public class LoanDropdownReadPlatformServiceImpl 
implements LoanDropdownReadPlat
         return 
Arrays.asList(preCloseInterestCalculationStrategy(LoanPreClosureInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE),
                 
preCloseInterestCalculationStrategy(LoanPreClosureInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE));
     }
+
+    @Override
+    public List<EnumOptionData> retrieveRepaymentStartDateTypeOptions() {
+        return 
Arrays.asList(repaymentStartDateType(RepaymentStartDateType.DISBURSEMENT_DATE),
+                
repaymentStartDateType(RepaymentStartDateType.SUBMITTED_ON_DATE));
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index 67ad626fc..d8e890604 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -229,7 +229,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
                     + "lp.disallow_expected_disbursements as 
disallowExpectedDisbursements, lp.allow_approved_disbursed_amounts_over_applied 
as allowApprovedDisbursedAmountsOverApplied, lp.over_applied_calculation_type 
as overAppliedCalculationType, over_applied_number as overAppliedNumber, "
                     + "lp.days_in_month_enum as daysInMonth, 
lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as 
isInterestRecalculationEnabled, "
                     + "lp.can_define_fixed_emi_amount as 
canDefineInstallmentAmount, lp.instalment_amount_in_multiples_of as 
installmentAmountInMultiplesOf, "
-                    + "lp.due_days_for_repayment_event as 
dueDaysForRepaymentEvent, lp.overdue_days_for_repayment_event as 
overDueDaysForRepaymentEvent, lp.enable_down_payment as enableDownPayment, 
lp.disbursed_amount_percentage_for_down_payment as 
disbursedAmountPercentageForDownPayment, 
lp.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment, 
"
+                    + "lp.due_days_for_repayment_event as 
dueDaysForRepaymentEvent, lp.overdue_days_for_repayment_event as 
overDueDaysForRepaymentEvent, lp.enable_down_payment as enableDownPayment, 
lp.disbursed_amount_percentage_for_down_payment as 
disbursedAmountPercentageForDownPayment, 
lp.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment, 
lp.repayment_start_date_type_enum as repaymentStartDateType, "
                     + "lpr.pre_close_interest_calculation_strategy as 
preCloseInterestCalculationStrategy, "
                     + "lpr.id as lprId, lpr.product_id as productId, 
lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as 
rescheduleStrategy, "
                     + "lpr.rest_frequency_type_enum as restFrequencyEnum, 
lpr.rest_frequency_interval as restFrequencyInterval, "
@@ -364,6 +364,8 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
             final boolean enableDownPayment = 
rs.getBoolean("enableDownPayment");
             final BigDecimal disbursedAmountPercentageForDownPayment = 
rs.getBigDecimal("disbursedAmountPercentageForDownPayment");
             final boolean enableAutoRepaymentForDownPayment = 
rs.getBoolean("enableAutoRepaymentForDownPayment");
+            final Integer repaymentStartDateTypeId = 
JdbcSupport.getInteger(rs, "repaymentStartDateType");
+            final EnumOptionData repaymentStartDateType = 
LoanEnumerations.repaymentStartDateType(repaymentStartDateTypeId);
 
             String status = "";
             if (closeDate != null && 
closeDate.isBefore(DateUtils.getBusinessLocalDate())) {
@@ -521,7 +523,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
                     maximumGap, syncExpectedWithDisbursementDate, 
canUseForTopup, isEqualAmortization, rateOptions, this.rates,
                     isRatesEnabled, fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket,
                     dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, 
enableDownPayment, disbursedAmountPercentageForDownPayment,
-                    enableAutoRepaymentForDownPayment, advancedPaymentData);
+                    enableAutoRepaymentForDownPayment, advancedPaymentData, 
repaymentStartDateType);
         }
     }
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java
new file mode 100644
index 000000000..c13939f80
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java
@@ -0,0 +1,474 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.time.LocalDate;
+import java.util.HashMap;
+import java.util.UUID;
+import org.apache.fineract.client.models.GetDelinquencyBucketsResponse;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
+import org.apache.fineract.client.models.PutLoanProductsProductIdResponse;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import 
org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class LoanProductRepaymentStartDateConfigurationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private ClientHelper clientHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+    }
+
+    @Test
+    public void 
loanProductWithRepaymentStartDateTypeConfigurationCreateAndModifyTest() {
+        // create product with repayment start date configuration, get , modify
+
+        // Delinquency Bucket
+        final Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = 
DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketId);
+
+        final Integer repaymentStartDateType = 2;
+
+        // create loan product with repayment start date configuration
+        Integer loanProductId = 
createLoanProductWithRepaymentStartDateTypeConfiguration(loanTransactionHelper, 
delinquencyBucketId,
+                repaymentStartDateType);
+
+        GetLoanProductsProductIdResponse getLoanProductsProductResponse = 
loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(getLoanProductsProductResponse);
+        assertEquals(repaymentStartDateType, 
getLoanProductsProductResponse.getRepaymentStartDateType().getId().intValue());
+        assertEquals("repaymentStartDateType.submittedOnDate", 
getLoanProductsProductResponse.getRepaymentStartDateType().getCode());
+
+        // modify loan product repayment start date configuration to 
disbursement date
+
+        PutLoanProductsProductIdResponse loanProductModifyResponse = 
updateLoanProduct(loanTransactionHelper,
+                getLoanProductsProductResponse.getId());
+        assertNotNull(loanProductModifyResponse);
+
+        getLoanProductsProductResponse = 
loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(getLoanProductsProductResponse);
+        assertEquals(1, 
getLoanProductsProductResponse.getRepaymentStartDateType().getId().intValue());
+        assertEquals("repaymentStartDateType.disbursementDate", 
getLoanProductsProductResponse.getRepaymentStartDateType().getCode());
+
+    }
+
+    @Test
+    public void 
loanProductWithNoRepaymentStartDateTypeConfigurationDefaultsToDisbursementDateTest()
 {
+        // create loan product with no configuration for repayment start date 
and verify that it is disbursement date by
+        // default
+        // Delinquency Bucket
+        final Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = 
DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketId);
+
+        final Integer repaymentStartDateType = null;
+
+        // create loan product with repayment start date configuration
+        Integer loanProductId = 
createLoanProductWithRepaymentStartDateTypeConfiguration(loanTransactionHelper, 
delinquencyBucketId,
+                repaymentStartDateType);
+
+        GetLoanProductsProductIdResponse getLoanProductsProductResponse = 
loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(getLoanProductsProductResponse);
+        assertEquals(1, 
getLoanProductsProductResponse.getRepaymentStartDateType().getId().intValue());
+        assertEquals("repaymentStartDateType.disbursementDate", 
getLoanProductsProductResponse.getRepaymentStartDateType().getCode());
+    }
+
+    @Test
+    public void 
loanAccountWithLoanProductRepaymentStartDateTypeAsSubmittedOnDateScheduleTest() 
{
+        // create loan account with product with repayment start date type 
configuration as submitted on date, verify
+        // repayment schedule is according to submitted on date, before and 
after disbursements
+        try {
+
+            // Set business date
+            LocalDate businessDate = LocalDate.of(2023, 03, 3);
+
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, businessDate);
+
+            // Loan ExternalId
+            String loanExternalIdStr = UUID.randomUUID().toString();
+
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+            // set repayment start date type as submittedOn date
+            final Integer repaymentStartDateType = 2;
+
+            // Loan Product creation with repayment start date type 
configuration
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithRepaymentStartDateTypeConfigurationAndMultipleDisbursements(
+                    loanTransactionHelper, repaymentStartDateType);
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(repaymentStartDateType, 
getLoanProductsProductResponse.getRepaymentStartDateType().getId().intValue());
+            assertEquals("repaymentStartDateType.submittedOnDate", 
getLoanProductsProductResponse.getRepaymentStartDateType().getCode());
+
+            // create loan account with submitted date as business date (03 
March 2023) and expected disbursement date
+            // as future date (07 March 2023)
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(),
+                    loanExternalIdStr);
+
+            // Retrieve Loan with loanId
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails);
+
+            // verify loan schedule is according to submitted on date
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+            // loan term
+            assertEquals(92, 
loanDetails.getRepaymentSchedule().getLoanTermInDays());
+
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+
+            // verify amounts
+            assertEquals(1000.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalExpected());
+
+            // first period [2023-03-03 to 2023-04-03]
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+
+            // second period [2023-04-03 to 2023-05-03]
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+
+            // third period [2023-05-03 to 2023-06-03]
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 06, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(333.34, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+
+            // first disbursement on a future date (7 March 2023)
+
+            LocalDate disbursementDate = LocalDate.of(2023, 03, 7);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, disbursementDate);
+
+            loanTransactionHelper.disburseLoanWithTransactionAmount("07 March 
2023", loanId, "500");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            // verify loan schedule is according to submitted on date after 
first disbursement
+            assertNotNull(loanDetails);
+            assertNotNull(loanDetails.getRepaymentSchedule());
+            // loan term
+            assertEquals(92, 
loanDetails.getRepaymentSchedule().getLoanTermInDays());
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+
+            // verify amounts
+            assertEquals(500.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalExpected());
+            assertEquals(500.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalDisbursed());
+
+            // first period [2023-03-03 to 2023-04-03]
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+
+            // second period [2023-04-03 to 2023-05-03]
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+
+            // third period [2023-05-03 to 2023-06-03]
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 06, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(166.66, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+
+            // second disbursement next month (7 April 2023)
+
+            disbursementDate = LocalDate.of(2023, 04, 7);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, disbursementDate);
+
+            loanTransactionHelper.disburseLoanWithTransactionAmount("07 April 
2023", loanId, "500");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            // verify loan schedule is according to submitted on date after 
second disbursement
+
+            assertNotNull(loanDetails);
+            assertNotNull(loanDetails.getRepaymentSchedule());
+            // loan term
+            assertEquals(92, 
loanDetails.getRepaymentSchedule().getLoanTermInDays());
+            assertEquals(5, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+
+            // verify amounts
+            assertEquals(1000.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalExpected());
+            assertEquals(1000.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalDisbursed());
+
+            // first period [2023-03-03 to 2023-04-03]
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+
+            // second period [2023-04-03 to 2023-05-03]
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+
+            // third period [2023-05-03 to 2023-06-03]
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 06, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    @Test
+    public void 
loanAccountWithLoanProductRepaymentStartDateTypeAsDisbursementDateScheduleTest()
 {
+        // create loan account with loan product with repayment start date 
type configuration as disbursement date ,
+        // verify repayment schedule is as per disbursement date before and 
after disbursements
+
+        try {
+
+            // Set business date
+            LocalDate businessDate = LocalDate.of(2023, 03, 3);
+
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, businessDate);
+
+            // Loan ExternalId
+            String loanExternalIdStr = UUID.randomUUID().toString();
+
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+            // set repayment start date type as default, disbursement date
+            final Integer repaymentStartDateType = 1;
+
+            // Loan Product creation with repayment date type configuration
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithRepaymentStartDateTypeConfigurationAndMultipleDisbursements(
+                    loanTransactionHelper, repaymentStartDateType);
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(repaymentStartDateType, 
getLoanProductsProductResponse.getRepaymentStartDateType().getId().intValue());
+            assertEquals("repaymentStartDateType.disbursementDate", 
getLoanProductsProductResponse.getRepaymentStartDateType().getCode());
+
+            // create loan account with submitted date as business date (03 
March 2023) and expected disbursement date
+            // (07 March 2023)
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(),
+                    loanExternalIdStr);
+
+            // Retrieve Loan with loanId
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails);
+
+            // verify loan schedule is according to disbursement date
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // loan term
+            assertEquals(92, 
loanDetails.getRepaymentSchedule().getLoanTermInDays());
+
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+
+            // verify amounts
+            assertEquals(1000.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalExpected());
+
+            // first period [2023-03-07 to 2023-04-07]
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+
+            // second period [2023-04-07 to 2023-05-07]
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+
+            // third period [2023-05-07 to 2023-06-07]
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 05, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 06, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(333.34, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+
+            // first disbursement (7 March 2023)
+
+            LocalDate disbursementDate = LocalDate.of(2023, 03, 7);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, disbursementDate);
+
+            loanTransactionHelper.disburseLoanWithTransactionAmount("07 March 
2023", loanId, "500");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            // verify loan schedule is according to disbursement date
+            assertNotNull(loanDetails);
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // loan term
+            assertEquals(92, 
loanDetails.getRepaymentSchedule().getLoanTermInDays());
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+
+            // verify amounts
+            assertEquals(500.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalExpected());
+            assertEquals(500.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalDisbursed());
+
+            // first period [2023-03-07 to 2023-04-07]
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+
+            // second period [2023-04-07 to 2023-05-07]
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+
+            // third period [2023-05-07 to 2023-06-07]
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 05, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 06, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(166.66, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+
+            // second disbursement next month (7 April 2023)
+
+            disbursementDate = LocalDate.of(2023, 04, 7);
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, disbursementDate);
+
+            loanTransactionHelper.disburseLoanWithTransactionAmount("07 April 
2023", loanId, "500");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            // verify loan schedule is according to disbursement after second 
disbursement
+
+            assertNotNull(loanDetails);
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // loan term
+            assertEquals(92, 
loanDetails.getRepaymentSchedule().getLoanTermInDays());
+            assertEquals(5, 
loanDetails.getRepaymentSchedule().getPeriods().size());
+
+            // verify amounts
+            assertEquals(1000.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalExpected());
+            assertEquals(1000.0, 
loanDetails.getRepaymentSchedule().getTotalPrincipalDisbursed());
+
+            // first period [2023-03-07 to 2023-04-07]
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+
+            // second period [2023-04-07 to 2023-05-07]
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(333.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+
+            // third period [2023-05-07 to 2023-06-07]
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 05, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 06, 7), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(333.34, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    private PutLoanProductsProductIdResponse 
updateLoanProduct(LoanTransactionHelper loanTransactionHelper, Long id) {
+        // repayment start date configuration
+        final Integer repaymentStartDateType = 1;
+        final PutLoanProductsProductIdRequest requestModifyLoan = new 
PutLoanProductsProductIdRequest()
+                .repaymentStartDateType(repaymentStartDateType).locale("en");
+        return loanTransactionHelper.updateLoanProduct(id, requestModifyLoan);
+    }
+
+    private Integer 
createLoanProductWithRepaymentStartDateTypeConfiguration(final 
LoanTransactionHelper loanTransactionHelper,
+            final Integer delinquencyBucketId, final Integer 
repaymentStartDateType) {
+        final HashMap<String, Object> loanProductMap = new 
LoanProductTestBuilder().withRepaymentStartDateType(repaymentStartDateType)
+                .build(null, delinquencyBucketId);
+        final Integer loanProductId = 
loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
+        return loanProductId;
+
+    }
+
+    private Integer createLoanAccountMultipleRepaymentsDisbursement(final 
Integer clientID, final Long loanProductID,
+            final String externalId) {
+
+        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("3")
+                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("3").withRepaymentEveryAfter("1")
+                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
+                
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
+                .withExpectedDisbursementDate("07 March 
2023").withSubmittedOnDate("03 March 2023").withLoanType("individual")
+                .withExternalId(externalId).build(clientID.toString(), 
loanProductID.toString(), null);
+
+        final Integer loanId = 
loanTransactionHelper.getLoanId(loanApplicationJSON);
+        loanTransactionHelper.approveLoan("03 March 2023", "1000", loanId, 
null);
+        return loanId;
+    }
+
+    private GetLoanProductsProductIdResponse 
createLoanProductWithRepaymentStartDateTypeConfigurationAndMultipleDisbursements(
+            LoanTransactionHelper loanTransactionHelper, final Integer 
repaymentStartDateType) {
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+                
.withRepaymentAfterEvery("1").withNumberOfRepayments("3").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+                
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance()
+                
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365")
+                .withMoratorium("0", 
"0").withMultiDisburse().withDisallowExpectedDisbursements(true)
+                
.withRepaymentStartDateType(repaymentStartDateType).build(null);
+        final Integer loanProductId = 
loanTransactionHelper.getLoanProductId(loanProductJSON);
+        return loanTransactionHelper.getLoanProduct(loanProductId);
+    }
+
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index 411c0b1c6..c648c544c 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -149,6 +149,7 @@ public class LoanProductTestBuilder {
     private boolean enableDownPayment = false;
     private String disbursedAmountPercentageForDownPayment = null;
     private boolean enableAutoRepaymentForDownPayment = false;
+    private Integer repaymentStartDateType = null;
 
     public String build() {
         final HashMap<String, Object> map = build(null, null);
@@ -299,6 +300,10 @@ public class LoanProductTestBuilder {
             map.put("enableAutoRepaymentForDownPayment", 
enableAutoRepaymentForDownPayment);
         }
 
+        if (this.repaymentStartDateType != null) {
+            map.put("repaymentStartDateType", repaymentStartDateType);
+        }
+
         return map;
     }
 
@@ -723,4 +728,9 @@ public class LoanProductTestBuilder {
         return this;
     }
 
+    public LoanProductTestBuilder withRepaymentStartDateType(final Integer 
repaymentStartDateType) {
+        this.repaymentStartDateType = repaymentStartDateType;
+        return this;
+    }
+
 }

Reply via email to