FINERACT-2312: full term configuration added

This commit is contained in:
Attila Budai
2025-12-08 19:08:29 +01:00
committed by Adam Saghy
parent 035b90211c
commit a3fdfb920d
23 changed files with 492 additions and 100 deletions
@@ -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")
@@ -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)
@@ -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");
@@ -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);
}
@@ -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);
}
}
}
@@ -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);
}
@@ -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);
}
@@ -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,
@@ -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>
@@ -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>
@@ -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;
@@ -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;
}
}