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 a3fdfb920d FINERACT-2312: full term configuration added
a3fdfb920d is described below

commit a3fdfb920d8857d10a6255746aee26d784963bff
Author: Attila Budai <[email protected]>
AuthorDate: Mon Dec 1 15:44:42 2025 +0100

    FINERACT-2312: full term configuration added
---
 .../loanaccount/api/LoanApiConstants.java          |   2 +
 .../loanaccount/data/LoanAccountData.java          |  16 +-
 .../portfolio/loanaccount/domain/Loan.java         |  26 ++-
 .../loanproduct/LoanProductConstants.java          |   1 +
 .../api/LoanProductsApiResourceSwagger.java        |   6 +
 .../loanproduct/data/LoanProductData.java          | 133 ++++++++-------
 .../portfolio/loanproduct/domain/LoanProduct.java  |   9 +-
 .../domain/LoanProductTrancheDetails.java          |   6 +-
 .../loanaccount/api/LoansApiResourceSwagger.java   |   8 +
 .../serialization/LoanApplicationValidator.java    |  27 ++-
 .../loanaccount/service/LoanAssemblerImpl.java     |  20 ++-
 .../loanaccount/service/LoanProductAssembler.java  |   5 +-
 .../LoanProductTrancheDetailsUpdateUtil.java       |   9 +
 .../service/LoanReadPlatformServiceImpl.java       |   5 +-
 .../serialization/LoanProductDataValidator.java    |  40 ++++-
 .../LoanProductReadPlatformServiceImpl.java        |  22 +--
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 .../parts/0207_add_allow_full_term_for_tranche.xml |  39 +++++
 .../portfolio/loanaccount/domain/LoanBuilder.java  |   8 +-
 .../LoanProductValidationStepDefinitions.java      |   2 +-
 .../LoanDisbursementDetailsIntegrationTest.java    | 187 +++++++++++++++++++++
 .../common/loans/LoanApplicationTestBuilder.java   |   9 +
 .../common/loans/LoanProductTestBuilder.java       |  11 ++
 23 files changed, 492 insertions(+), 100 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index fa651a64cc..ace70a9b59 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -195,6 +195,8 @@ public interface LoanApiConstants {
 
     String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = 
"interestRecognitionOnDisbursementDate";
 
+    String ALLOW_FULL_TERM_FOR_TRANCHE = "allowFullTermForTranche";
+
     // Loan Summary Transaction Types
     List<LoanTransactionType> LOAN_SUMMARY_TRANSACTION_TYPES = 
List.of(LoanTransactionType.CHARGE_ADJUSTMENT, //
             LoanTransactionType.CHARGEBACK, //
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
index bca5e59bd2..d97a52b9d1 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
@@ -269,6 +269,7 @@ public class LoanAccountData {
     private Boolean enableInstallmentLevelDelinquency;
     private LocalDate lastClosedBusinessDate;
     private Boolean chargedOff;
+    private Boolean allowFullTermForTranche;
 
     private Boolean enableDownPayment;
     private BigDecimal disbursedAmountPercentageForDownPayment;
@@ -480,12 +481,12 @@ public class LoanAccountData {
             final BigDecimal disbursedAmountPercentageForDownPayment, final 
boolean enableAutoRepaymentForDownPayment,
             final boolean enableInstallmentLevelDelinquency, final 
EnumOptionData loanScheduleType,
             final EnumOptionData loanScheduleProcessingType, final Integer 
fixedLength, final StringEnumOptionData chargeOffBehaviour,
-            final boolean isInterestRecognitionOnDisbursementDate, final 
StringEnumOptionData daysInYearCustomStrategy,
-            final boolean enableIncomeCapitalization, final 
StringEnumOptionData capitalizedIncomeCalculationType,
-            final StringEnumOptionData capitalizedIncomeStrategy, 
StringEnumOptionData capitalizedIncomeType,
-            final boolean enableBuyDownFee, final StringEnumOptionData 
buyDownFeeCalculationType,
-            final StringEnumOptionData buyDownFeeStrategy, final 
StringEnumOptionData buyDownFeeIncomeType,
-            final boolean merchantBuyDownFee) {
+            final boolean isInterestRecognitionOnDisbursementDate, final 
boolean allowFullTermForTranche,
+            final StringEnumOptionData daysInYearCustomStrategy, final boolean 
enableIncomeCapitalization,
+            final StringEnumOptionData capitalizedIncomeCalculationType, final 
StringEnumOptionData capitalizedIncomeStrategy,
+            StringEnumOptionData capitalizedIncomeType, final boolean 
enableBuyDownFee,
+            final StringEnumOptionData buyDownFeeCalculationType, final 
StringEnumOptionData buyDownFeeStrategy,
+            final StringEnumOptionData buyDownFeeIncomeType, final boolean 
merchantBuyDownFee) {
 
         final CollectionData delinquent = CollectionData.template();
 
@@ -531,7 +532,8 @@ public class LoanAccountData {
                 
.setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency).setLoanScheduleType(loanScheduleType)
                 
.setLoanScheduleProcessingType(loanScheduleProcessingType).setFixedLength(fixedLength)
                 
.setChargeOffBehaviour(chargeOffBehaviour).setInterestRecognitionOnDisbursementDate(isInterestRecognitionOnDisbursementDate)
-                
.setDaysInYearCustomStrategy(daysInYearCustomStrategy).setEnableIncomeCapitalization(enableIncomeCapitalization)
+                
.setAllowFullTermForTranche(allowFullTermForTranche).setDaysInYearCustomStrategy(daysInYearCustomStrategy)
+                .setEnableIncomeCapitalization(enableIncomeCapitalization)
                 
.setCapitalizedIncomeCalculationType(capitalizedIncomeCalculationType)
                 
.setCapitalizedIncomeStrategy(capitalizedIncomeStrategy).setCapitalizedIncomeType(capitalizedIncomeType)
                 
.setEnableBuyDownFee(enableBuyDownFee).setBuyDownFeeCalculationType(buyDownFeeCalculationType)
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 601b8c6bba..d8bfda8bc6 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
@@ -427,6 +427,9 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
     @Column(name = "enable_installment_level_delinquency", nullable = false)
     private boolean enableInstallmentLevelDelinquency = false;
 
+    @Column(name = "allow_full_term_for_tranche", nullable = false)
+    private boolean allowFullTermForTranche = false;
+
     public static Loan newIndividualLoanApplication(final String accountNo, 
final Client client, final AccountType loanType,
             final LoanProduct loanProduct, final Fund fund, final Staff 
officer, final CodeValue loanPurpose,
             final LoanRepaymentScheduleTransactionProcessor 
transactionProcessingStrategy,
@@ -436,12 +439,12 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
             final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
-            final LocalDate submittedOnDate) {
+            final LocalDate submittedOnDate, final Boolean 
allowFullTermForTranche) {
         return new Loan(accountNo, client, null, loanType, fund, officer, 
loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, null, loanCharges, collateral, 
null, fixedEmiAmount, disbursementDetails,
                 maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential, rates,
                 fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
-                submittedOnDate);
+                submittedOnDate, allowFullTermForTranche);
     }
 
     public static Loan newGroupLoanApplication(final String accountNo, final 
Group group, final AccountType loanType,
@@ -453,12 +456,12 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
             final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
-            final LocalDate submittedOnDate) {
+            final LocalDate submittedOnDate, final Boolean 
allowFullTermForTranche) {
         return new Loan(accountNo, null, group, loanType, fund, officer, 
loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, null, loanCharges, null, 
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
                 maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential, rates,
                 fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
-                submittedOnDate);
+                submittedOnDate, allowFullTermForTranche);
     }
 
     public static Loan newIndividualLoanApplicationFromGroup(final String 
accountNo, final Client client, final Group group,
@@ -470,12 +473,12 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
             final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
-            final LocalDate submittedOnDate) {
+            final LocalDate submittedOnDate, final Boolean 
allowFullTermForTranche) {
         return new Loan(accountNo, client, group, loanType, fund, officer, 
loanPurpose, transactionProcessingStrategy, loanProduct,
                 loanRepaymentScheduleDetail, null, loanCharges, null, 
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails,
                 maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential, rates,
                 fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
-                submittedOnDate);
+                submittedOnDate, allowFullTermForTranche);
     }
 
     protected Loan() {
@@ -491,7 +494,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             final Boolean createStandingInstructionAtDisbursement, final 
Boolean isFloatingInterestRate,
             final BigDecimal interestRateDifferential, final List<Rate> rates, 
final BigDecimal fixedPrincipalPercentagePerInstallment,
             final ExternalId externalId, final LoanApplicationTerms 
loanApplicationTerms, final Boolean enableInstallmentLevelDelinquency,
-            final LocalDate submittedOnDate) {
+            final LocalDate submittedOnDate, final Boolean 
allowFullTermForTranche) {
         this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
 
         this.isFloatingInterestRate = isFloatingInterestRate;
@@ -572,6 +575,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
             this.loanInterestRecalculationDetails.updateLoan(this);
         }
         this.enableInstallmentLevelDelinquency = 
enableInstallmentLevelDelinquency;
+        this.allowFullTermForTranche = allowFullTermForTranche;
         this.getLoanProductRelatedDetail()
                 
.setEnableAccrualActivityPosting(loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
     }
@@ -1783,6 +1787,14 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         this.enableInstallmentLevelDelinquency = 
enableInstallmentLevelDelinquency;
     }
 
+    public boolean isAllowFullTermForTranche() {
+        return this.allowFullTermForTranche;
+    }
+
+    public void updateAllowFullTermForTranche(boolean allowFullTermForTranche) 
{
+        this.allowFullTermForTranche = allowFullTermForTranche;
+    }
+
     public void deductFromNetDisbursalAmount(final BigDecimal subtrahend) {
         this.netDisbursalAmount = this.netDisbursalAmount.subtract(subtrahend);
     }
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 7a3784a395..9039ad0bb7 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
@@ -72,6 +72,7 @@ public interface LoanProductConstants {
     String MULTI_DISBURSE_LOAN_PARAMETER_NAME = "multiDisburseLoan";
     String MAX_TRANCHE_COUNT_PARAMETER_NAME = "maxTrancheCount";
     String OUTSTANDING_LOAN_BALANCE_PARAMETER_NAME = "outstandingLoanBalance";
+    String ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME = "allowFullTermForTranche";
 
     String GRACE_ON_ARREARS_AGEING_PARAMETER_NAME = "graceOnArrearsAgeing";
     String OVERDUE_DAYS_FOR_NPA_PARAMETER_NAME = "overdueDaysForNPA";
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 7dc3a16c34..e558aedf7d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -309,6 +309,8 @@ public final class LoanProductsApiResourceSwagger {
         // Multi Disburse
         @Schema(example = "true")
         public Boolean multiDisburseLoan;
+        @Schema(example = "false", description = "Allow full term length for 
each tranche disbursement. Only available for PROGRESSIVE schedule type with 
multi-disbursement enabled.")
+        public Boolean allowFullTermForTranche;
         @Schema(example = "50")
         public Integer principalThresholdForLastInstallment;
         @Schema(example = "true")
@@ -1481,6 +1483,8 @@ public final class LoanProductsApiResourceSwagger {
         public Boolean isRatesEnabled;
         @Schema(example = "true")
         public Boolean multiDisburseLoan;
+        @Schema(example = "false", description = "Allow full term length for 
each tranche disbursement. Only available for PROGRESSIVE schedule type with 
multi-disbursement enabled.")
+        public Boolean allowFullTermForTranche;
         @Schema(example = "3")
         public Integer maxTrancheCount;
         @Schema(example = "36000.000000")
@@ -1794,6 +1798,8 @@ public final class LoanProductsApiResourceSwagger {
         // Multi Disburse
         @Schema(example = "true")
         public Boolean multiDisburseLoan;
+        @Schema(example = "false", description = "Allow full term length for 
each tranche disbursement. Only available for PROGRESSIVE schedule type with 
multi-disbursement enabled.")
+        public Boolean allowFullTermForTranche;
         @Schema(example = "50")
         public Integer principalThresholdForLastInstallment;
         @Schema(example = "true")
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index cb6edc56cc..8a4e2598de 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -217,6 +217,7 @@ public class LoanProductData implements Serializable {
     private final Boolean allowApprovedDisbursedAmountsOverApplied;
     private final String overAppliedCalculationType;
     private final Integer overAppliedNumber;
+    private final Boolean allowFullTermForTranche;
 
     private final BigDecimal principalThresholdForLastInstallment;
 
@@ -332,6 +333,7 @@ public class LoanProductData implements Serializable {
         final Boolean allowApprovedDisbursedAmountsOverApplied = false;
         final String overAppliedCalculationType = null;
         final Integer overAppliedNumber = null;
+        final Boolean allowFullTermForTranche = null;
 
         final LoanProductGuaranteeData productGuaranteeData = null;
         final boolean holdGuaranteeFunds = false;
@@ -392,21 +394,21 @@ public class LoanProductData implements Serializable {
                 includeInBorrowerCycle, useBorrowerCycle, startDate, 
closeDate, status, externalId, principalVariations,
                 interestRateVariations, numberOfRepaymentVariations, 
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
                 disallowExpectedDisbursements, 
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, 
overAppliedNumber,
-                graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, 
daysInYearType, isInterestRecalculationEnabled,
-                interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, 
productGuaranteeData,
-                principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
-                installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, 
floatingRateId,
-                floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
-                maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, 
minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
-                fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
-                overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation, creditAllocation, repaymentStartDateType, 
enableInstallmentLevelDelinquency, loanScheduleType,
-                loanScheduleProcessingType, fixedLength, 
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
-                interestRecognitionOnDisbursementDate, 
daysInYearTypeCustomStrategy, enableIncomeCapitalization,
-                capitalizedIncomeCalculationType, capitalizedIncomeStrategy, 
capitalizedIncomeType, enableBuyDownFee,
-                buyDownFeeCalculationType, buyDownFeeStrategy, 
buyDownFeeIncomeType, merchantBuyDownFee, writeOffReasonsToExpenseMappings,
-                writeOffReasonOptions);
+                allowFullTermForTranche, graceOnArrearsAgeing, 
overdueDaysForNPA, daysInMonthType, daysInYearType,
+                isInterestRecalculationEnabled, interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds,
+                productGuaranteeData, principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion,
+                canDefineInstallmentAmount, installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes,
+                isLinkedToFloatingInterestRates, floatingRateId, 
floatingRateName, interestRateDifferential, minDifferentialLendingRate,
+                defaultDifferentialLendingRate, maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed,
+                isVariableInstallmentsAllowed, minimumGap, maximumGap, 
syncExpectedWithDisbursementDate, canUseForTopup,
+                isEqualAmortization, rateOptions, rates, isRatesEnabled, 
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions,
+                delinquencyBucket, dueDaysForRepaymentEvent, 
overDueDaysForRepaymentEvent, enableDownPayment,
+                disbursedAmountPercentageDownPayment, 
enableAutoRepaymentForDownPayment, paymentAllocation, creditAllocation,
+                repaymentStartDateType, enableInstallmentLevelDelinquency, 
loanScheduleType, loanScheduleProcessingType, fixedLength,
+                enableAccrualActivityPosting, supportedInterestRefundTypes, 
chargeOffBehaviour, interestRecognitionOnDisbursementDate,
+                daysInYearTypeCustomStrategy, enableIncomeCapitalization, 
capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
+                capitalizedIncomeType, enableBuyDownFee, 
buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType,
+                merchantBuyDownFee, writeOffReasonsToExpenseMappings, 
writeOffReasonOptions);
 
     }
 
@@ -473,6 +475,7 @@ public class LoanProductData implements Serializable {
         final Boolean allowApprovedDisbursedAmountsOverApplied = false;
         final String overAppliedCalculationType = null;
         final Integer overAppliedNumber = null;
+        final Boolean allowFullTermForTranche = null;
 
         final EnumOptionData daysInMonthType = null;
         final EnumOptionData daysInYearType = null;
@@ -532,21 +535,21 @@ public class LoanProductData implements Serializable {
                 includeInBorrowerCycle, useBorrowerCycle, startDate, 
closeDate, status, externalId, principalVariations,
                 interestRateVariations, numberOfRepaymentVariations, 
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
                 disallowExpectedDisbursements, 
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, 
overAppliedNumber,
-                graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, 
daysInYearType, isInterestRecalculationEnabled,
-                interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, 
productGuaranteeData,
-                principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
-                installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, 
floatingRateId,
-                floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
-                maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, 
minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
-                fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
-                overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation, creditAllocation, repaymentStartDateType, 
enableInstallmentLevelDelinquency, loanScheduleType,
-                loanScheduleProcessingType, fixedLength, 
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
-                interestRecognitionOnDisbursementDate, 
daysInYearTypeCustomStrategy, enableIncomeCapitalization,
-                capitalizedIncomeCalculationType, capitalizedIncomeStrategy, 
capitalizedIncomeType, enableBuyDownFee,
-                buyDownFeeCalculationType, buyDownFeeStrategy, 
buyDownFeeIncomeType, merchantBuyDownFee, writeOffReasonsToExpenseMappings,
-                writeOffReasonOptions);
+                allowFullTermForTranche, graceOnArrearsAgeing, 
overdueDaysForNPA, daysInMonthType, daysInYearType,
+                isInterestRecalculationEnabled, interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds,
+                productGuaranteeData, principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion,
+                canDefineInstallmentAmount, installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes,
+                isLinkedToFloatingInterestRates, floatingRateId, 
floatingRateName, interestRateDifferential, minDifferentialLendingRate,
+                defaultDifferentialLendingRate, maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed,
+                isVariableInstallmentsAllowed, minimumGap, maximumGap, 
syncExpectedWithDisbursementDate, canUseForTopup,
+                isEqualAmortization, rateOptions, rates, isRatesEnabled, 
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions,
+                delinquencyBucket, dueDaysForRepaymentEvent, 
overDueDaysForRepaymentEvent, enableDownPayment,
+                disbursedAmountPercentageDownPayment, 
enableAutoRepaymentForDownPayment, paymentAllocation, creditAllocation,
+                repaymentStartDateType, enableInstallmentLevelDelinquency, 
loanScheduleType, loanScheduleProcessingType, fixedLength,
+                enableAccrualActivityPosting, supportedInterestRefundTypes, 
chargeOffBehaviour, interestRecognitionOnDisbursementDate,
+                daysInYearTypeCustomStrategy, enableIncomeCapitalization, 
capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
+                capitalizedIncomeType, enableBuyDownFee, 
buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType,
+                merchantBuyDownFee, writeOffReasonsToExpenseMappings, 
writeOffReasonOptions);
 
     }
 
@@ -619,6 +622,7 @@ public class LoanProductData implements Serializable {
         final Boolean allowApprovedDisbursedAmountsOverApplied = false;
         final String overAppliedCalculationType = null;
         final Integer overAppliedNumber = null;
+        final Boolean allowFullTermForTranche = null;
 
         final EnumOptionData daysInMonthType = 
CommonEnumerations.daysInMonthType(DaysInMonthType.ACTUAL);
         final EnumOptionData daysInYearType = 
CommonEnumerations.daysInYearType(DaysInYearType.ACTUAL);
@@ -679,21 +683,21 @@ public class LoanProductData implements Serializable {
                 includeInBorrowerCycle, useBorrowerCycle, startDate, 
closeDate, status, externalId, principalVariationsForBorrowerCycle,
                 interestRateVariationsForBorrowerCycle, 
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount,
                 outstandingLoanBalance, disallowExpectedDisbursements, 
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType,
-                overAppliedNumber, graceOnArrearsAgeing, overdueDaysForNPA, 
daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
-                interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, 
productGuaranteeData,
-                principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
-                installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, 
floatingRateId,
-                floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
-                maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, 
minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
-                fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
-                overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation, creditAllocation, repaymentStartDateType, 
enableInstallmentLevelDelinquency, loanScheduleType,
-                loanScheduleProcessingType, fixedLength, 
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
-                interestRecognitionOnDisbursementDate, 
daysInYearTypeCustomStrategy, enableIncomeCapitalization,
-                capitalizedIncomeCalculationType, capitalizedIncomeStrategy, 
capitalizedIncomeType, enableBuyDownFee,
-                buyDownFeeCalculationType, buyDownFeeStrategy, 
buyDownFeeIncomeType, merchantBuyDownFee, writeOffReasonsToExpenseMappings,
-                writeOffReasonOptions);
+                overAppliedNumber, allowFullTermForTranche, 
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
+                isInterestRecalculationEnabled, interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds,
+                productGuaranteeData, principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion,
+                canDefineInstallmentAmount, installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes,
+                isLinkedToFloatingInterestRates, floatingRateId, 
floatingRateName, interestRateDifferential, minDifferentialLendingRate,
+                defaultDifferentialLendingRate, maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed,
+                isVariableInstallmentsAllowed, minimumGap, maximumGap, 
syncExpectedWithDisbursementDate, canUseForTopup,
+                isEqualAmortization, rateOptions, rates, isRatesEnabled, 
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions,
+                delinquencyBucket, dueDaysForRepaymentEvent, 
overDueDaysForRepaymentEvent, enableDownPayment,
+                disbursedAmountPercentageDownPayment, 
enableAutoRepaymentForDownPayment, paymentAllocation, creditAllocation,
+                repaymentStartDateType, enableInstallmentLevelDelinquency, 
loanScheduleType, loanScheduleProcessingType, fixedLength,
+                enableAccrualActivityPosting, supportedInterestRefundTypes, 
chargeOffBehaviour, interestRecognitionOnDisbursementDate,
+                daysInYearTypeCustomStrategy, enableIncomeCapitalization, 
capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
+                capitalizedIncomeType, enableBuyDownFee, 
buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType,
+                merchantBuyDownFee, writeOffReasonsToExpenseMappings, 
writeOffReasonOptions);
 
     }
 
@@ -760,6 +764,7 @@ public class LoanProductData implements Serializable {
         final Boolean allowApprovedDisbursedAmountsOverApplied = false;
         final String overAppliedCalculationType = null;
         final Integer overAppliedNumber = null;
+        final Boolean allowFullTermForTranche = null;
 
         final EnumOptionData daysInMonthType = 
CommonEnumerations.daysInMonthType(DaysInMonthType.ACTUAL);
         final EnumOptionData daysInYearType = 
CommonEnumerations.daysInYearType(DaysInYearType.ACTUAL);
@@ -820,21 +825,21 @@ public class LoanProductData implements Serializable {
                 includeInBorrowerCycle, useBorrowerCycle, startDate, 
closeDate, status, externalId, principalVariationsForBorrowerCycle,
                 interestRateVariationsForBorrowerCycle, 
numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount,
                 outstandingLoanBalance, disallowExpectedDisbursements, 
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType,
-                overAppliedNumber, graceOnArrearsAgeing, overdueDaysForNPA, 
daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
-                interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, 
productGuaranteeData,
-                principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
-                installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, 
floatingRateId,
-                floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
-                maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, 
minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, rates, isRatesEnabled,
-                fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
-                overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
-                paymentAllocation, creditAllocationData, 
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
-                loanScheduleProcessingType, fixedLength, 
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
-                interestRecognitionOnDisbursementDate, 
daysInYearTypeCustomStrategy, enableIncomeCapitalization,
-                capitalizedIncomeCalculationType, capitalizedIncomeStrategy, 
capitalizedIncomeType, enableBuyDownFee,
-                buyDownFeeCalculationType, buyDownFeeStrategy, 
buyDownFeeIncomeType, merchantBuyDownFee, writeOffReasonsToExpenseMappings,
-                writeOffReasonOptions);
+                overAppliedNumber, allowFullTermForTranche, 
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
+                isInterestRecalculationEnabled, interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds,
+                productGuaranteeData, principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion,
+                canDefineInstallmentAmount, installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes,
+                isLinkedToFloatingInterestRates, floatingRateId, 
floatingRateName, interestRateDifferential, minDifferentialLendingRate,
+                defaultDifferentialLendingRate, maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed,
+                isVariableInstallmentsAllowed, minimumGap, maximumGap, 
syncExpectedWithDisbursementDate, canUseForTopup,
+                isEqualAmortization, rateOptions, rates, isRatesEnabled, 
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions,
+                delinquencyBucket, dueDaysForRepaymentEvent, 
overDueDaysForRepaymentEvent, enableDownPayment,
+                disbursedAmountPercentageDownPayment, 
enableAutoRepaymentForDownPayment, paymentAllocation, creditAllocationData,
+                repaymentStartDateType, enableInstallmentLevelDelinquency, 
loanScheduleType, loanScheduleProcessingType, fixedLength,
+                enableAccrualActivityPosting, supportedInterestRefundTypes, 
chargeOffBehaviour, interestRecognitionOnDisbursementDate,
+                daysInYearTypeCustomStrategy, enableIncomeCapitalization, 
capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
+                capitalizedIncomeType, enableBuyDownFee, 
buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType,
+                merchantBuyDownFee, writeOffReasonsToExpenseMappings, 
writeOffReasonOptions);
     }
 
     public static LoanProductData withAccountingDetails(final LoanProductData 
productData, final Map<String, Object> accountingMappings,
@@ -873,9 +878,9 @@ public class LoanProductData implements Serializable {
             Collection<LoanProductBorrowerCycleVariationData> 
numberOfRepaymentVariations, Boolean multiDisburseLoan,
             Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final 
Boolean disallowExpectedDisbursements,
             final Boolean allowApprovedDisbursedAmountsOverApplied, final 
String overAppliedCalculationType,
-            final Integer overAppliedNumber, final Integer 
graceOnArrearsAgeing, final Integer overdueDaysForNPA,
-            final EnumOptionData daysInMonthType, final EnumOptionData 
daysInYearType, final boolean isInterestRecalculationEnabled,
-            final LoanProductInterestRecalculationData 
interestRecalculationData,
+            final Integer overAppliedNumber, final Boolean 
allowFullTermForTranche, final Integer graceOnArrearsAgeing,
+            final Integer overdueDaysForNPA, final EnumOptionData 
daysInMonthType, final EnumOptionData daysInYearType,
+            final boolean isInterestRecalculationEnabled, final 
LoanProductInterestRecalculationData interestRecalculationData,
             final Integer minimumDaysBetweenDisbursalAndFirstRepayment, 
boolean holdGuaranteeFunds,
             final LoanProductGuaranteeData loanProductGuaranteeData, final 
BigDecimal principalThresholdForLastInstallment,
             final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean 
canDefineInstallmentAmount,
@@ -1000,6 +1005,7 @@ public class LoanProductData implements Serializable {
         this.allowApprovedDisbursedAmountsOverApplied = 
allowApprovedDisbursedAmountsOverApplied;
         this.overAppliedCalculationType = overAppliedCalculationType;
         this.overAppliedNumber = overAppliedNumber;
+        this.allowFullTermForTranche = allowFullTermForTranche;
 
         this.graceOnArrearsAgeing = graceOnArrearsAgeing;
         this.overdueDaysForNPA = overdueDaysForNPA;
@@ -1189,6 +1195,7 @@ public class LoanProductData implements Serializable {
         this.allowApprovedDisbursedAmountsOverApplied = 
productData.allowApprovedDisbursedAmountsOverApplied;
         this.overAppliedCalculationType = 
productData.overAppliedCalculationType;
         this.overAppliedNumber = productData.overAppliedNumber;
+        this.allowFullTermForTranche = productData.allowFullTermForTranche;
 
         this.minimumDaysBetweenDisbursalAndFirstRepayment = 
productData.minimumDaysBetweenDisbursalAndFirstRepayment;
 
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 b4d3da9896..9f79209630 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
@@ -288,7 +288,7 @@ public class LoanProduct extends 
AbstractPersistableCustom<Long> {
             final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy, 
final LoanCapitalizedIncomeType capitalizedIncomeType,
             final boolean enableBuyDownFee, final 
LoanBuyDownFeeCalculationType buyDownFeeCalculationType,
             final LoanBuyDownFeeStrategy buyDownFeeStrategy, final 
LoanBuyDownFeeIncomeType buyDownFeeIncomeType,
-            final boolean merchantBuyDownFee) {
+            final boolean merchantBuyDownFee, final boolean 
allowFullTermForTranche) {
         this.fund = fund;
         this.transactionProcessingStrategyCode = 
transactionProcessingStrategyCode;
 
@@ -364,7 +364,8 @@ public class LoanProduct extends 
AbstractPersistableCustom<Long> {
             loanConfigurableAttributes.updateLoanProduct(this);
         }
 
-        this.loanProductTrancheDetails = new 
LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, 
outstandingLoanBalance);
+        this.loanProductTrancheDetails = new 
LoanProductTrancheDetails(multiDisburseLoan, maxTrancheCount, 
outstandingLoanBalance,
+                allowFullTermForTranche);
         this.overdueDaysForNPA = overdueDaysForNPA;
         this.productInterestRecalculationDetails = 
productInterestRecalculationDetails;
         this.minimumDaysBetweenDisbursalAndFirstRepayment = 
minimumDaysBetweenDisbursalAndFirstRepayment;
@@ -763,4 +764,8 @@ public class LoanProduct extends 
AbstractPersistableCustom<Long> {
         this.enableInstallmentLevelDelinquency = 
enableInstallmentLevelDelinquency;
     }
 
+    public boolean isAllowFullTermForTranche() {
+        return this.loanProductTrancheDetails != null && 
this.loanProductTrancheDetails.isAllowFullTermForTranche();
+    }
+
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductTrancheDetails.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductTrancheDetails.java
index 2a7142adcb..5a552f63fa 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductTrancheDetails.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProductTrancheDetails.java
@@ -38,15 +38,19 @@ public class LoanProductTrancheDetails {
     @Column(name = "max_outstanding_loan_balance", scale = 6, precision = 19)
     private BigDecimal outstandingLoanBalance;
 
+    @Column(name = "allow_full_term_for_tranche")
+    private boolean allowFullTermForTranche;
+
     protected LoanProductTrancheDetails() {
         // TODO Auto-generated constructor stub
     }
 
     public LoanProductTrancheDetails(final boolean multiDisburseLoan, final 
Integer maxTrancheCount,
-            final BigDecimal outstandingLoanBalance) {
+            final BigDecimal outstandingLoanBalance, final boolean 
allowFullTermForTranche) {
         this.multiDisburseLoan = multiDisburseLoan;
         this.maxTrancheCount = maxTrancheCount;
         this.outstandingLoanBalance = outstandingLoanBalance;
+        this.allowFullTermForTranche = allowFullTermForTranche;
     }
 
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index 40d97e3fc4..8d97966ca6 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -603,6 +603,8 @@ final class LoansApiResourceSwagger {
             public String writeoffReason;
             public GetLoansLoanIdLinkedAccount linkedAccount;
             public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
+            @Schema(example = "false", description = "Allow full term length 
for each tranche disbursement")
+            public Boolean allowFullTermForTranche;
             @Schema(example = "1100.000000")
             public BigDecimal fixedEmiAmount;
             @Schema(example = "35000.000000")
@@ -1198,6 +1200,8 @@ final class LoansApiResourceSwagger {
         public List<GetLoansLoanIdTransactions> transactions;
         @Schema(description = "Set of GetLoansLoanIdDisbursementDetails")
         public Set<GetLoansLoanIdDisbursementDetails> disbursementDetails;
+        @Schema(example = "false", description = "Allow full term length for 
each tranche disbursement")
+        public Boolean allowFullTermForTranche;
         @Schema(description = "Delinquent data")
         public GetLoansLoanIdDelinquencySummary delinquent;
         @Schema(description = "Set of charges")
@@ -1344,6 +1348,8 @@ final class LoansApiResourceSwagger {
         public String externalId;
         @Schema(description = "List of PostLoansDisbursementData")
         public List<PostLoansDisbursementData> disbursementData;
+        @Schema(example = "false", description = "Allow full term length for 
each tranche disbursement")
+        public Boolean allowFullTermForTranche;
         @Schema(description = "Maximum allowed outstanding balance")
         public BigDecimal maxOutstandingLoanBalance;
         @Schema(example = "[2011, 10, 20]")
@@ -1550,6 +1556,8 @@ final class LoansApiResourceSwagger {
         public List<PutLoansLoanIdChanges> charges;
         public List<PutLoansLoanIdCollateral> collateral;
         public List<PutLoansLoanIdDisbursementData> disbursementData;
+        @Schema(example = "false", description = "Allow full term length for 
each tranche disbursement")
+        public Boolean allowFullTermForTranche;
         @Schema(example = "HORIZONTAL")
         public String loanScheduleProcessingType;
         @Schema(example = "false")
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
index 10d4c3f8dc..6401f9957f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
@@ -175,7 +175,8 @@ public final class LoanApplicationValidator {
             LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, 
LoanProductConstants.FIXED_LENGTH,
             LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, 
LoanProductConstants.ENABLE_DOWN_PAYMENT,
             LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, 
LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
-            LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, 
LoanApiConstants.daysInYearCustomStrategyParameterName));
+            LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, 
LoanApiConstants.daysInYearCustomStrategyParameterName,
+            LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE));
     public static final String LOANAPPLICATION_UNDO = "loanapplication.undo";
 
     private final FromJsonHelper fromApiJsonHelper;
@@ -324,6 +325,18 @@ public final class LoanApplicationValidator {
                 }
             }
 
+            if 
(this.fromApiJsonHelper.parameterExists(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE,
 element)) {
+                final Boolean allowFullTermForTranche = this.fromApiJsonHelper
+                        
.extractBooleanNamed(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE, element);
+                
baseDataValidator.reset().parameter(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE).value(allowFullTermForTranche)
+                        .ignoreIfNull().validateForBooleanValue();
+
+                if (Boolean.TRUE.equals(allowFullTermForTranche) && 
!loanProduct.isAllowFullTermForTranche()) {
+                    
baseDataValidator.reset().parameter(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE).failWithCode("not.allowed.by.product",
+                            "Full term tranche cannot be enabled because the 
loan product does not allow it");
+                }
+            }
+
             BigDecimal fixedPrincipalPercentagePerInstallment = 
this.fromApiJsonHelper
                     
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
 element);
             
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName)
@@ -946,6 +959,18 @@ public final class LoanApplicationValidator {
                 }
             }
 
+            if 
(this.fromApiJsonHelper.parameterExists(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE,
 element)) {
+                final Boolean allowFullTermForTranche = this.fromApiJsonHelper
+                        
.extractBooleanNamed(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE, element);
+                
baseDataValidator.reset().parameter(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE).value(allowFullTermForTranche)
+                        .ignoreIfNull().validateForBooleanValue();
+
+                if (Boolean.TRUE.equals(allowFullTermForTranche) && 
!loanProduct.isAllowFullTermForTranche()) {
+                    
baseDataValidator.reset().parameter(LoanApiConstants.ALLOW_FULL_TERM_FOR_TRANCHE).failWithCode("not.allowed.by.product",
+                            "Full term tranche cannot be enabled because the 
loan product does not allow it");
+                }
+            }
+
             BigDecimal fixedPrincipalPercentagePerInstallment = 
this.fromApiJsonHelper
                     
.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName,
 element);
             
baseDataValidator.reset().parameter(LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName)
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
index 0f251096b4..9944ac03b7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java
@@ -248,6 +248,12 @@ public class LoanAssemblerImpl implements LoanAssembler {
             isEnableInstallmentLevelDelinquency = 
loanProduct.isEnableInstallmentLevelDelinquency();
         }
 
+        Boolean allowFullTermForTranche = this.fromApiJsonHelper
+                
.extractBooleanNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME,
 element);
+        if (allowFullTermForTranche == null) {
+            allowFullTermForTranche = loanProduct.isAllowFullTermForTranche();
+        }
+
         final boolean isHolidayEnabled = 
this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
         Long officeId = client != null ? client.getOffice().getId() : 
group.getOffice().getId();
         final List<Holiday> holidays = 
this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
@@ -262,19 +268,19 @@ public class LoanAssemblerImpl implements LoanAssembler {
                     syncDisbursementWithMeeting, fixedEmiAmount, 
disbursementDetails, maxOutstandingLoanBalance,
                     createStandingInstructionAtDisbursement, 
isFloatingInterestRate, interestRateDifferential, rates,
                     fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, isEnableInstallmentLevelDelinquency,
-                    submittedOnDate);
+                    submittedOnDate, allowFullTermForTranche);
         } else if (group != null) {
             loanApplication = Loan.newGroupLoanApplication(accountNo, group, 
loanAccountType, loanProduct, fund, loanOfficer, loanPurpose,
                     transactionProcessingStrategy, loanProductRelatedDetail, 
loanCharges, syncDisbursementWithMeeting, fixedEmiAmount,
                     disbursementDetails, maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate,
                     interestRateDifferential, rates, 
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms,
-                    isEnableInstallmentLevelDelinquency, submittedOnDate);
+                    isEnableInstallmentLevelDelinquency, submittedOnDate, 
allowFullTermForTranche);
         } else if (client != null) {
             loanApplication = Loan.newIndividualLoanApplication(accountNo, 
client, loanAccountType, loanProduct, fund, loanOfficer,
                     loanPurpose, transactionProcessingStrategy, 
loanProductRelatedDetail, loanCharges, collateral, fixedEmiAmount,
                     disbursementDetails, maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate,
                     interestRateDifferential, rates, 
fixedPrincipalPercentagePerInstallment, externalId, loanApplicationTerms,
-                    isEnableInstallmentLevelDelinquency, submittedOnDate);
+                    isEnableInstallmentLevelDelinquency, submittedOnDate, 
allowFullTermForTranche);
         } else {
             throw new IllegalStateException("No loan application exists for 
either a client or group (or both).");
         }
@@ -843,6 +849,14 @@ public class LoanAssemblerImpl implements LoanAssembler {
             
loan.updateEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency);
         }
 
+        // update allow full term for tranche
+        if 
(command.isChangeInBooleanParameterNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME,
+                loan.isAllowFullTermForTranche())) {
+            final Boolean allowFullTermForTranche = command
+                    
.booleanObjectValueOfParameterNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME);
+            loan.updateAllowFullTermForTranche(allowFullTermForTranche);
+        }
+
         if (changes.containsKey("recalculateLoanSchedule")) {
             changes.remove("recalculateLoanSchedule");
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
index 07644c8d55..ddc728a975 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductAssembler.java
@@ -267,6 +267,9 @@ public class LoanProductAssembler {
 
         final Integer overAppliedNumber = 
command.integerValueOfParameterNamed(LoanProductConstants.OVER_APPLIED_NUMBER);
 
+        final boolean allowFullTermForTranche = 
command.parameterExists(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME)
+                && 
command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME);
+
         final Integer dueDaysForRepaymentEvent = 
command.integerValueOfParameterNamed(LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT);
         final Integer overDueDaysForRepaymentEvent = command
                 
.integerValueOfParameterNamed(LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT);
@@ -353,7 +356,7 @@ public class LoanProductAssembler {
                 enableAccrualActivityPosting, supportedInterestRefundTypes, 
chargeOffBehaviour, interestRecognitionOnDisbursementDate,
                 daysInYearCustomStrategy, enableIncomeCapitalization, 
capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
                 capitalizedIncomeType, enableBuyDownFee, 
buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType,
-                merchantBuyDownFee);
+                merchantBuyDownFee, allowFullTermForTranche);
 
     }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductTrancheDetailsUpdateUtil.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductTrancheDetailsUpdateUtil.java
index 045097cc2f..37f4dfd8c6 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductTrancheDetailsUpdateUtil.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductTrancheDetailsUpdateUtil.java
@@ -54,9 +54,18 @@ public class LoanProductTrancheDetailsUpdateUtil {
                 
actualChanges.put(LoanProductConstants.OUTSTANDING_LOAN_BALANCE_PARAMETER_NAME, 
newValue);
                 loanProductTrancheDetails.setOutstandingLoanBalance(newValue);
             }
+
+            if 
(command.isChangeInBooleanParameterNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME,
+                    loanProductTrancheDetails.isAllowFullTermForTranche())) {
+                final boolean newValue = command
+                        
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME);
+                
actualChanges.put(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME, 
newValue);
+                loanProductTrancheDetails.setAllowFullTermForTranche(newValue);
+            }
         } else {
             loanProductTrancheDetails.setMaxTrancheCount(null);
             loanProductTrancheDetails.setOutstandingLoanBalance(null);
+            loanProductTrancheDetails.setAllowFullTermForTranche(false);
         }
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 9b05453977..cc8595aa50 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -865,7 +865,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                     + " topup.topup_amount as topupAmount, 
l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as 
overpaidOnDate, "
                     + " l.is_charged_off as isChargedOff, 
l.charge_off_reason_cv_id as chargeOffReasonId, codec.code_value as 
chargeOffReason, l.charged_off_on_date as chargedOffOnDate, 
l.enable_down_payment as enableDownPayment, 
l.disbursed_amount_percentage_for_down_payment as 
disbursedAmountPercentageForDownPayment, 
l.enable_auto_repayment_for_down_payment as enableAutoRepaymentForDownPayment,"
                     + " cobu.username as chargedOffByUsername, cobu.firstname 
as chargedOffByFirstname, cobu.lastname as chargedOffByLastname, 
l.loan_schedule_type as loanScheduleType, l.loan_schedule_processing_type as 
loanScheduleProcessingType, "
-                    + " l.charge_off_behaviour as chargeOffBehaviour, 
l.interest_recognition_on_disbursement_date as 
interestRecognitionOnDisbursementDate " //
+                    + " l.charge_off_behaviour as chargeOffBehaviour, 
l.interest_recognition_on_disbursement_date as 
interestRecognitionOnDisbursementDate, l.allow_full_term_for_tranche as 
allowFullTermForTranche " //
                     + " from m_loan l" //
                     + " join m_product_loan lp on lp.id = l.product_id" //
                     + " left join m_loan_recalculation_details lir on 
lir.loan_id = l.id join m_currency rc on rc."
@@ -1238,6 +1238,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
             final Integer fixedLength = JdbcSupport.getInteger(rs, 
"fixedLength");
             final LoanChargeOffBehaviour chargeOffBehaviour = 
LoanChargeOffBehaviour.valueOf(rs.getString("chargeOffBehaviour"));
             final boolean interestRecognitionOnDisbursementDate = 
rs.getBoolean("interestRecognitionOnDisbursementDate");
+            final boolean allowFullTermForTranche = 
rs.getBoolean("allowFullTermForTranche");
             final StringEnumOptionData daysInYearCustomStrategy = 
ApiFacingEnum.getStringEnumOptionData(DaysInYearCustomStrategyType.class,
                     rs.getString("daysInYearCustomStrategy"));
             final boolean enableIncomeCapitalization = 
rs.getBoolean("enableIncomeCapitalization");
@@ -1274,7 +1275,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                     lastClosedBusinessDate, overpaidOnDate, isChargedOff, 
enableDownPayment, disbursedAmountPercentageForDownPayment,
                     enableAutoRepaymentForDownPayment, 
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(),
                     loanScheduleProcessingType.asEnumOptionData(), 
fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(),
-                    interestRecognitionOnDisbursementDate, 
daysInYearCustomStrategy, enableIncomeCapitalization,
+                    interestRecognitionOnDisbursementDate, 
allowFullTermForTranche, daysInYearCustomStrategy, enableIncomeCapitalization,
                     capitalizedIncomeCalculationType, 
capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee,
                     buyDownFeeCalculationType, buyDownFeeStrategy, 
buyDownFeeIncomeType, merchantBuyDownFee);
         }
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 2bc26a6b7c..49242644d4 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
@@ -208,7 +208,8 @@ public final class LoanProductDataValidator {
             LoanProductAccountingParams.BUY_DOWN_EXPENSE.getValue(), 
LoanProductAccountingParams.INCOME_FROM_BUY_DOWN.getValue(),
             LoanProductConstants.MERCHANT_BUY_DOWN_FEE_PARAM_NAME,
             
LoanProductAccountingParams.CAPITALIZED_INCOME_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue(),
 //
-            
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue()
 //
+            
LoanProductAccountingParams.BUYDOWN_FEE_CLASSIFICATION_TO_INCOME_ACCOUNT_MAPPINGS.getValue(),
 //
+            LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME //
     ));
 
     private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { 
LoanProductConstants.amortizationTypeParamName,
@@ -1062,6 +1063,43 @@ public final class LoanProductDataValidator {
             
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull().inMinMaxRange(0,
 1);
         }
 
+        // Determine effective values for allowFullTermForTranche validation
+        // For updates, fall back to existing product values if not in request
+        Boolean effectiveMultiDisburseLoan = multiDisburseLoan;
+        if 
(!this.fromApiJsonHelper.parameterExists(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME,
 element)
+                && loanProduct != null) {
+            effectiveMultiDisburseLoan = loanProduct.isMultiDisburseLoan();
+        }
+
+        String effectiveLoanScheduleType = 
LoanScheduleType.CUMULATIVE.toString();
+        if 
(fromApiJsonHelper.parameterExists(LoanProductConstants.LOAN_SCHEDULE_TYPE, 
element)) {
+            effectiveLoanScheduleType = 
fromApiJsonHelper.extractStringNamed(LoanProductConstants.LOAN_SCHEDULE_TYPE, 
element);
+        } else if (loanProduct != null) {
+            effectiveLoanScheduleType = 
loanProduct.getLoanProductRelatedDetail().getLoanScheduleType().toString();
+        }
+
+        Boolean effectiveAllowFullTermForTranche = false;
+        if 
(this.fromApiJsonHelper.parameterExists(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME,
 element)) {
+            effectiveAllowFullTermForTranche = this.fromApiJsonHelper
+                    
.extractBooleanNamed(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME,
 element);
+            
baseDataValidator.reset().parameter(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME)
+                    
.value(effectiveAllowFullTermForTranche).ignoreIfNull().validateForBooleanValue();
+        } else if (loanProduct != null && 
loanProduct.getLoanProductTrancheDetails() != null) {
+            effectiveAllowFullTermForTranche = 
loanProduct.getLoanProductTrancheDetails().isAllowFullTermForTranche();
+        }
+
+        // Validate: allowFullTermForTranche requires multi-disburse and 
PROGRESSIVE schedule
+        if (Boolean.TRUE.equals(effectiveAllowFullTermForTranche)) {
+            if (!Boolean.TRUE.equals(effectiveMultiDisburseLoan)) {
+                
baseDataValidator.reset().parameter(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME).failWithCode(
+                        "requires.multi.disburse.loan", "Full term tranche can 
only be enabled for multi-disbursement loan products");
+            }
+            if 
(!LoanScheduleType.PROGRESSIVE.toString().equals(effectiveLoanScheduleType)) {
+                
baseDataValidator.reset().parameter(LoanProductConstants.ALLOW_FULL_TERM_FOR_TRANCHE_PARAM_NAME).failWithCode(
+                        "requires.progressive.schedule.type", "Full term 
tranche can only be enabled for PROGRESSIVE loan schedule type");
+            }
+        }
+
         final String overAppliedCalculationType = 
this.fromApiJsonHelper.extractStringNamed(OVER_APPLIED_CALCULATION_TYPE, 
element);
         
baseDataValidator.reset().parameter(OVER_APPLIED_CALCULATION_TYPE).value(overAppliedCalculationType).notExceedingLengthOf(10);
     }
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 4444f42ade..d10879c143 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
@@ -254,7 +254,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
                     + "lp.min_days_between_disbursal_and_first_repayment As 
minimumDaysBetweenDisbursalAndFirstRepayment, "
                     + "lp.amortization_method_enum as amortizationMethod, 
lp.arrearstolerance_amount as tolerance, "
                     + "lp.accounting_type as accountingType, 
lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as 
useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate,  "
-                    + "lp.allow_multiple_disbursals as multiDisburseLoan, 
lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as 
outstandingLoanBalance, "
+                    + "lp.allow_multiple_disbursals as multiDisburseLoan, 
lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as 
outstandingLoanBalance, lp.allow_full_term_for_tranche as 
allowFullTermForTranche, "
                     + "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.installment_amount_in_multiples_of as 
installmentAmountInMultiplesOf, "
@@ -439,6 +439,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
             final Boolean allowApprovedDisbursedAmountsOverApplied = 
rs.getBoolean("allowApprovedDisbursedAmountsOverApplied");
             final String overAppliedCalculationType = 
rs.getString("overAppliedCalculationType");
             final Integer overAppliedNumber = rs.getInt("overAppliedNumber");
+            final Boolean allowFullTermForTranche = 
rs.getBoolean("allowFullTermForTranche");
 
             final int daysInMonth = JdbcSupport.getInteger(rs, "daysInMonth");
             final EnumOptionData daysInMonthType = 
CommonEnumerations.daysInMonthType(daysInMonth);
@@ -591,15 +592,16 @@ public class LoanProductReadPlatformServiceImpl 
implements LoanProductReadPlatfo
                     principalVariationsForBorrowerCycle, 
interestRateVariationsForBorrowerCycle,
                     numberOfRepaymentVariationsForBorrowerCycle, 
multiDisburseLoan, maxTrancheCount, outstandingLoanBalance,
                     disallowExpectedDisbursements, 
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, 
overAppliedNumber,
-                    graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, 
daysInYearType, isInterestRecalculationEnabled,
-                    interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, 
loanProductGuaranteeData,
-                    principalThresholdForLastInstallment, 
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
-                    installmentAmountInMultiplesOf, allowAttributeOverrides, 
isLinkedToFloatingInterestRates, floatingRateId,
-                    floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
-                    maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, 
minimumGap,
-                    maximumGap, syncExpectedWithDisbursementDate, 
canUseForTopup, isEqualAmortization, rateOptions, this.rates,
-                    isRatesEnabled, fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket,
-                    dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, 
enableDownPayment, disbursedAmountPercentageForDownPayment,
+                    allowFullTermForTranche, graceOnArrearsAgeing, 
overdueDaysForNPA, daysInMonthType, daysInYearType,
+                    isInterestRecalculationEnabled, interestRecalculationData, 
minimumDaysBetweenDisbursalAndFirstRepayment,
+                    holdGuaranteeFunds, loanProductGuaranteeData, 
principalThresholdForLastInstallment,
+                    accountMovesOutOfNPAOnlyOnArrearsCompletion, 
canDefineInstallmentAmount, installmentAmountInMultiplesOf,
+                    allowAttributeOverrides, isLinkedToFloatingInterestRates, 
floatingRateId, floatingRateName, interestRateDifferential,
+                    minDifferentialLendingRate, 
defaultDifferentialLendingRate, maxDifferentialLendingRate,
+                    isFloatingInterestRateCalculationAllowed, 
isVariableIntallmentsAllowed, minimumGap, maximumGap,
+                    syncExpectedWithDisbursementDate, canUseForTopup, 
isEqualAmortization, rateOptions, this.rates, isRatesEnabled,
+                    fixedPrincipalPercentagePerInstallment, 
delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
+                    overDueDaysForRepaymentEvent, enableDownPayment, 
disbursedAmountPercentageForDownPayment,
                     enableAutoRepaymentForDownPayment, advancedPaymentData, 
creditAllocationData, repaymentStartDateType,
                     enableInstallmentLevelDelinquency, 
loanScheduleType.asEnumOptionData(), 
loanScheduleProcessingType.asEnumOptionData(),
                     fixedLength, enableAccrualActivityPosting, 
supportedInterestRefundTypes,
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index b5dabf2d0a..2b164480b9 100644
--- 
a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -225,4 +225,5 @@
     <include 
file="parts/0204_transaction_summary_with_asset_owner_and_from_asset_owner_id_for_buybacks.xml"
 relativeToChangelogFile="true" />
     <include file="parts/0205_add_read_familymembers_permission.xml" 
relativeToChangelogFile="true" />
     <include 
file="parts/0206_transaction_summary_with_asset_owner_classification_name_bug_fix.xml"
 relativeToChangelogFile="true" />
+    <include file="parts/0207_add_allow_full_term_for_tranche.xml" 
relativeToChangelogFile="true" />
 </databaseChangeLog>
diff --git 
a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0207_add_allow_full_term_for_tranche.xml
 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0207_add_allow_full_term_for_tranche.xml
new file mode 100644
index 0000000000..80b0041d83
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0207_add_allow_full_term_for_tranche.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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";>
+    <changeSet author="fineract" id="1">
+        <addColumn tableName="m_product_loan">
+            <column defaultValueBoolean="false" type="boolean" 
name="allow_full_term_for_tranche">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <addColumn tableName="m_loan">
+            <column defaultValueBoolean="false" type="boolean" 
name="allow_full_term_for_tranche">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
index fc5a720942..1db4500c5f 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuilder.java
@@ -80,6 +80,7 @@ public class LoanBuilder {
     private ExternalId externalId = ExternalId.empty();
     private LoanApplicationTerms loanApplicationTerms = 
mock(LoanApplicationTerms.class);
     private Boolean enableInstallmentLevelDelinquency = false;
+    private Boolean allowFullTermForTranche = false;
     private LocalDate submittedOnDate = LocalDate.now(ZoneId.systemDefault());
     private LocalDate approvedOnDate;
     private LocalDate expectedDisbursementDate;
@@ -115,7 +116,7 @@ public class LoanBuilder {
                     transactionProcessor, loanRepaymentScheduleDetail, 
charges, collateral, fixedEmiAmount, disbursementDetails,
                     maxOutstandingLoanBalance, 
createStandingInstructionAtDisbursement, isFloatingInterestRate, 
interestRateDifferential,
                     rates, fixedPrincipalPercentagePerInstallment, externalId, 
loanApplicationTerms, enableInstallmentLevelDelinquency,
-                    submittedOnDate);
+                    submittedOnDate, allowFullTermForTranche);
 
             if (id != null) {
                 loan.setId(id);
@@ -338,6 +339,11 @@ public class LoanBuilder {
         return this;
     }
 
+    public LoanBuilder withAllowFullTermForTranche(Boolean 
allowFullTermForTranche) {
+        this.allowFullTermForTranche = allowFullTermForTranche;
+        return this;
+    }
+
     public LoanBuilder withSubmittedOnDate(LocalDate submittedOnDate) {
         this.submittedOnDate = submittedOnDate;
         return this;
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanproduct/LoanProductValidationStepDefinitions.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanproduct/LoanProductValidationStepDefinitions.java
index 35c071523c..e1e7297295 100644
--- 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanproduct/LoanProductValidationStepDefinitions.java
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanproduct/LoanProductValidationStepDefinitions.java
@@ -72,7 +72,7 @@ public class LoanProductValidationStepDefinitions implements 
En {
 
     private LoanProduct newLoanProduct(boolean allowMultipleDisbursals) {
         LoanProduct lp = new LoanProduct();
-        lp.setLoanProductTrancheDetails(new 
LoanProductTrancheDetails(allowMultipleDisbursals, null, null));
+        lp.setLoanProductTrancheDetails(new 
LoanProductTrancheDetails(allowMultipleDisbursals, null, null, false));
         return lp;
     }
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
index 996f31da22..6d683485b8 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDisbursementDetailsIntegrationTest.java
@@ -33,15 +33,19 @@ import java.math.BigDecimal;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.AdvancedPaymentData;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdDisbursementDetails;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PaymentAllocationOrder;
 import org.apache.fineract.client.models.PostLoansLoanIdResponse;
 import org.apache.fineract.client.models.PutLoansLoanIdResponse;
 import org.apache.fineract.integrationtests.common.ClientHelper;
@@ -53,6 +57,8 @@ import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
 import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
+import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -693,4 +699,185 @@ public class LoanDisbursementDetailsIntegrationTest {
                         
equalTo("error.msg.loan.disbursal.amount.can't.be.greater.than.maximum.applied.loan.amount.calculation"))
                 .expectStatusCode(403).build();
     }
+
+    private AdvancedPaymentData createDefaultPaymentAllocation(String 
futureInstallmentAllocationRule) {
+        AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
+        advancedPaymentData.setTransactionType("DEFAULT");
+        
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
+
+        List<PaymentAllocationOrder> paymentAllocationOrders = 
getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
+                PaymentAllocationType.PAST_DUE_FEE, 
PaymentAllocationType.PAST_DUE_PRINCIPAL, 
PaymentAllocationType.PAST_DUE_INTEREST,
+                PaymentAllocationType.DUE_PENALTY, 
PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
+                PaymentAllocationType.DUE_INTEREST, 
PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
+                PaymentAllocationType.IN_ADVANCE_PRINCIPAL, 
PaymentAllocationType.IN_ADVANCE_INTEREST);
+
+        advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
+        return advancedPaymentData;
+    }
+
+    private List<PaymentAllocationOrder> 
getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
+        AtomicInteger integer = new AtomicInteger(1);
+        return Arrays.stream(paymentAllocationTypes).map(pat -> {
+            PaymentAllocationOrder paymentAllocationOrder = new 
PaymentAllocationOrder();
+            paymentAllocationOrder.setPaymentAllocationRule(pat.name());
+            paymentAllocationOrder.setOrder(integer.getAndIncrement());
+            return paymentAllocationOrder;
+        }).toList();
+    }
+
+    @Test
+    public void testCreateLoanProductWithFullTermTrancheEnabled() {
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation("NEXT_INSTALLMENT");
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+                .withInterestTypeAsDecliningBalance().withMoratorium("", 
"").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+                
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
+                
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
+
+        final Integer loanProductId = 
this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+        log.info("------------------LOAN PRODUCT CREATED WITH ID----------- 
{}", loanProductId);
+
+        GetLoanProductsProductIdResponse loanProduct = 
this.loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(loanProduct);
+        assertEquals(true, loanProduct.getMultiDisburseLoan());
+        assertEquals(true, loanProduct.getAllowFullTermForTranche());
+        log.info("-------------------LOAN PRODUCT WITH allowFullTermForTranche 
CREATED SUCCESSFULLY-------");
+    }
+
+    @Test
+    public void 
testCreateLoanProductWithFullTermTrancheOnCumulativeShouldFail() {
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder()
+                .expectBody("userMessageGlobalisationCode", 
equalTo("validation.msg.validation.errors.exist"))
+                .expectBody("errors[0].userMessageGlobalisationCode",
+                        
equalTo("validation.msg.loanproduct.allowFullTermForTranche.requires.progressive.schedule.type"))
+                .expectStatusCode(400).build();
+
+        final LoanTransactionHelper validationErrorHelper = new 
LoanTransactionHelper(this.requestSpec, errorResponse);
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+                .withInterestTypeAsDecliningBalance().withMoratorium("", 
"").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+                
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.CUMULATIVE)
+                .withAllowFullTermForTranche(true).build(null);
+
+        validationErrorHelper.getLoanProductId(loanProductJSON);
+        log.info("-------------------LOAN PRODUCT WITH allowFullTermForTranche 
ON CUMULATIVE FAILED AS EXPECTED-------");
+    }
+
+    @Test
+    public void 
testCreateLoanProductWithFullTermTrancheOnSingleDisburseShouldFail() {
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation("NEXT_INSTALLMENT");
+
+        final ResponseSpecification errorResponse = new ResponseSpecBuilder()
+                .expectBody("userMessageGlobalisationCode", 
equalTo("validation.msg.validation.errors.exist"))
+                .expectBody("errors[0].userMessageGlobalisationCode",
+                        
equalTo("validation.msg.loanproduct.allowFullTermForTranche.requires.multi.disburse.loan"))
+                .expectStatusCode(400).build();
+
+        final LoanTransactionHelper validationErrorHelper = new 
LoanTransactionHelper(this.requestSpec, errorResponse);
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+                .withInterestTypeAsDecliningBalance().withMoratorium("", 
"").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+                
.withInterestTypeAsDecliningBalance().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
+                
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
+
+        validationErrorHelper.getLoanProductId(loanProductJSON);
+        log.info("-------------------LOAN PRODUCT WITH allowFullTermForTranche 
ON SINGLE DISBURSE FAILED AS EXPECTED-------");
+    }
+
+    @Test
+    public void testUpdateLoanProductPreservesAllowFullTermForTranche() {
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation("NEXT_INSTALLMENT");
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+                .withInterestTypeAsDecliningBalance().withMoratorium("", 
"").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+                
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
+                
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
+
+        final Integer loanProductId = 
this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+        log.info("------------------LOAN PRODUCT CREATED WITH ID----------- 
{}", loanProductId);
+
+        GetLoanProductsProductIdResponse loanProduct = 
this.loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(loanProduct);
+        assertEquals(true, loanProduct.getAllowFullTermForTranche());
+
+        org.apache.fineract.client.models.PutLoanProductsProductIdRequest 
updateRequest = new 
org.apache.fineract.client.models.PutLoanProductsProductIdRequest();
+        updateRequest.setDescription("Updated description");
+        this.loanTransactionHelper.updateLoanProduct((long) loanProductId, 
updateRequest);
+
+        GetLoanProductsProductIdResponse updatedProduct = 
this.loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(updatedProduct);
+        assertEquals(true, updatedProduct.getAllowFullTermForTranche());
+        assertEquals("Updated description", updatedProduct.getDescription());
+        log.info("-------------------LOAN PRODUCT UPDATE PRESERVED 
allowFullTermForTranche FLAG-------");
+    }
+
+    @Test
+    public void testLoanInheritsAllowFullTermForTrancheFromProduct() {
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation("NEXT_INSTALLMENT");
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+                .withInterestTypeAsDecliningBalance().withMoratorium("", 
"").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+                
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
+                
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
+
+        final Integer loanProductId = 
this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+        log.info("------------------LOAN PRODUCT CREATED WITH ID----------- 
{}", loanProductId);
+
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        log.info("------------------CLIENT CREATED WITH ID----------- {}", 
clientId);
+
+        List<HashMap> createTranches = new ArrayList<>();
+        
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 
March 2014", "5000"));
+        
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 
April 2014", "5000"));
+
+        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("10000").withLoanTermFrequency("12")
+                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
+                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("1").withExpectedDisbursementDate("01
 March 2014")
+                .withTranches(createTranches).withSubmittedOnDate("01 March 
2014")
+                
.withRepaymentStrategy(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)
+                .build(clientId.toString(), loanProductId.toString(), null);
+
+        final Integer loanId = 
this.loanTransactionHelper.getLoanId(loanApplicationJSON);
+        log.info("------------------LOAN CREATED WITH ID----------- {}", 
loanId);
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertNotNull(loanDetails);
+        assertEquals(true, loanDetails.getAllowFullTermForTranche());
+        log.info("-------------------LOAN INHERITED allowFullTermForTranche 
FROM PRODUCT SUCCESSFULLY-------");
+    }
+
+    @Test
+    public void testLoanLevelOverrideOfAllowFullTermForTranche() {
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation("NEXT_INSTALLMENT");
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments()
+                .withInterestTypeAsDecliningBalance().withMoratorium("", 
"").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
+                
.withInterestTypeAsDecliningBalance().withMultiDisburse().withLoanScheduleType(LoanScheduleType.PROGRESSIVE)
+                
.addAdvancedPaymentAllocation(defaultAllocation).withAllowFullTermForTranche(true).build(null);
+
+        final Integer loanProductId = 
this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+        log.info("------------------LOAN PRODUCT CREATED WITH ID----------- 
{}", loanProductId);
+
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
+        log.info("------------------CLIENT CREATED WITH ID----------- {}", 
clientId);
+
+        List<HashMap> createTranches = new ArrayList<>();
+        
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 
March 2014", "5000"));
+        
createTranches.add(this.loanTransactionHelper.createTrancheDetail(null, "01 
April 2014", "5000"));
+
+        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("10000").withLoanTermFrequency("12")
+                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
+                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("1").withExpectedDisbursementDate("01
 March 2014")
+                .withTranches(createTranches).withSubmittedOnDate("01 March 
2014")
+                
.withRepaymentStrategy(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY).withAllowFullTermForTranche(false)
+                .build(clientId.toString(), loanProductId.toString(), null);
+
+        final Integer loanId = 
this.loanTransactionHelper.getLoanId(loanApplicationJSON);
+        log.info("------------------LOAN CREATED WITH ID----------- {}", 
loanId);
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertNotNull(loanDetails);
+        assertEquals(false, loanDetails.getAllowFullTermForTranche());
+        log.info("-------------------LOAN LEVEL OVERRIDE OF 
allowFullTermForTranche WORKED SUCCESSFULLY-------");
+    }
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
index 469b06cb27..f7463eb3e2 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java
@@ -88,6 +88,7 @@ public class LoanApplicationTestBuilder {
     private boolean enableDownPayment = false;
     private boolean enableAutoRepaymentForDownPayment = false;
     private String disbursedAmountPercentageDownPayment;
+    private Boolean allowFullTermForTranche = null;
 
     public String build(final String clientID, final String groupID, final 
String loanProductId, final String savingsID) {
         final HashMap<String, Object> map = new HashMap<>();
@@ -220,6 +221,9 @@ public class LoanApplicationTestBuilder {
         if (disbursedAmountPercentageDownPayment != null) {
             map.put("disbursedAmountPercentageDownPayment", 
disbursedAmountPercentageDownPayment);
         }
+        if (allowFullTermForTranche != null) {
+            map.put("allowFullTermForTranche", allowFullTermForTranche);
+        }
         LOG.info("Loan Application request : {} ", map);
         return new Gson().toJson(map);
     }
@@ -468,4 +472,9 @@ public class LoanApplicationTestBuilder {
         return this;
     }
 
+    public LoanApplicationTestBuilder withAllowFullTermForTranche(final 
Boolean allowFullTermForTranche) {
+        this.allowFullTermForTranche = allowFullTermForTranche;
+        return this;
+    }
+
 }
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 8ae4bcc1df..2b706f6dbf 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
@@ -113,6 +113,7 @@ public class LoanProductTestBuilder {
     private Account feeAndPenaltyAssetAccount;
 
     private Boolean multiDisburseLoan = false;
+    private Boolean allowFullTermForTranche = false;
     private final String outstandingLoanBalance = "35000";
     private String maxTrancheCount = "3";
     private Boolean disallowExpectedDisbursements = false;
@@ -225,6 +226,7 @@ public class LoanProductTestBuilder {
         }
         if (this.multiDisburseLoan) {
             map.put("multiDisburseLoan", this.multiDisburseLoan);
+            map.put("allowFullTermForTranche", this.allowFullTermForTranche);
             map.put("maxTrancheCount", this.maxTrancheCount);
             map.put("outstandingLoanBalance", this.outstandingLoanBalance);
             map.put("disallowExpectedDisbursements", 
this.disallowExpectedDisbursements);
@@ -242,6 +244,10 @@ public class LoanProductTestBuilder {
             map.put("maxTrancheCount", this.maxTrancheCount);
             map.put("outstandingLoanBalance", this.outstandingLoanBalance);
         }
+        // Always send allowFullTermForTranche when it's true (for validation 
testing of single-disburse scenarios)
+        if (this.allowFullTermForTranche && !this.multiDisburseLoan) {
+            map.put("allowFullTermForTranche", this.allowFullTermForTranche);
+        }
 
         if (this.fullAccountingConfig != null) {
             map.putAll(this.fullAccountingConfig.toMap());
@@ -742,6 +748,11 @@ public class LoanProductTestBuilder {
         return this;
     }
 
+    public LoanProductTestBuilder withAllowFullTermForTranche(boolean 
allowFullTermForTranche) {
+        this.allowFullTermForTranche = allowFullTermForTranche;
+        return this;
+    }
+
     public LoanProductTestBuilder withFeeToIncomeAccountMapping(final Long 
chargeId, final Long accountId) {
         if (this.feeToIncomeAccountMappings == null) {
             this.feeToIncomeAccountMappings = new ArrayList<>();


Reply via email to