FINERACT-2311: Loan product - Buy down fees configuration

This commit is contained in:
adam.magyari
2025-06-24 15:06:54 +02:00
committed by Adam Saghy
parent 29d1ed3cd4
commit 1b1d6be956
23 changed files with 708 additions and 31 deletions
@@ -133,6 +133,9 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -1169,7 +1172,10 @@ public class LoansApiResource {
loanTermVariations, ApiFacingEnum.getValuesAsStringEnumOptionDataList(DaysInYearCustomStrategyType.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeCalculationType.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeStrategy.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeType.class));
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeType.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeCalculationType.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeStrategy.class),
ApiFacingEnum.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeIncomeType.class));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
@@ -1227,6 +1227,14 @@ final class LoansApiResourceSwagger {
public StringEnumOptionData capitalizedIncomeStrategy;
@Schema(example = "FEE")
public StringEnumOptionData capitalizedIncomeType;
@Schema(example = "false")
public Boolean enableBuyDownFee;
@Schema(example = "FLAT")
public StringEnumOptionData buyDownFeeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION")
public StringEnumOptionData buyDownFeeStrategy;
@Schema(example = "FEE")
public StringEnumOptionData buyDownFeeIncomeType;
}
@Schema(description = "GetLoansResponse")
@@ -1348,6 +1356,14 @@ final class LoansApiResourceSwagger {
public String capitalizedIncomeStrategy;
@Schema(example = "FEE")
public StringEnumOptionData capitalizedIncomeType;
@Schema(example = "false")
public Boolean enableBuyDownFee;
@Schema(example = "FLAT", allowableValues = "FLAT")
public String buyDownFeeCalculationType;
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
public String buyDownFeeStrategy;
@Schema(example = "FEE", allowableValues = { "FEE", "INTEREST" })
public String buyDownFeeIncomeType;
@Schema(example = "List of PostLoansDataTable")
public List<PostLoansDataTable> datatables;
@@ -550,7 +550,11 @@ public class LoanScheduleAssembler {
loanProduct.getLoanProductRelatedDetail().isEnableIncomeCapitalization(),
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeCalculationType(),
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy(),
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeType());
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeType(),
loanProduct.getLoanProductRelatedDetail().isEnableBuyDownFee(),
loanProduct.getLoanProductRelatedDetail().getBuyDownFeeCalculationType(),
loanProduct.getLoanProductRelatedDetail().getBuyDownFeeStrategy(),
loanProduct.getLoanProductRelatedDetail().getBuyDownFeeIncomeType());
}
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
@@ -40,6 +40,9 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.fund.domain.Fund;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -317,6 +320,14 @@ public class LoanProductAssembler {
final LoanCapitalizedIncomeType capitalizedIncomeType = command
.enumValueOfParameterNamed(LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME, LoanCapitalizedIncomeType.class);
final boolean enableBuyDownFee = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME);
final LoanBuyDownFeeCalculationType buyDownFeeCalculationType = command.enumValueOfParameterNamed(
LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, LoanBuyDownFeeCalculationType.class);
final LoanBuyDownFeeStrategy buyDownFeeStrategy = command
.enumValueOfParameterNamed(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanBuyDownFeeStrategy.class);
final LoanBuyDownFeeIncomeType buyDownFeeIncomeType = command
.enumValueOfParameterNamed(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, LoanBuyDownFeeIncomeType.class);
return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, loanProductCreditAllocationRules,
name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod,
@@ -338,7 +349,7 @@ public class LoanProductAssembler {
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate,
daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
capitalizedIncomeType);
capitalizedIncomeType, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType);
}
@@ -26,6 +26,9 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -328,6 +331,34 @@ public class LoanProductRelatedDetailUpdateUtil {
loanRepaymentScheduleDetail.setCapitalizedIncomeType(newValue);
}
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME,
loanRepaymentScheduleDetail.isEnableBuyDownFee())) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME);
actualChanges.put(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, newValue);
loanRepaymentScheduleDetail.setEnableBuyDownFee(newValue);
}
if (command.parameterExists(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME)) {
final LoanBuyDownFeeCalculationType newValue = command.enumValueOfParameterNamed(
LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, LoanBuyDownFeeCalculationType.class);
actualChanges.put(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, newValue);
loanRepaymentScheduleDetail.setBuyDownFeeCalculationType(newValue);
}
if (command.parameterExists(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME)) {
final LoanBuyDownFeeStrategy newValue = command.enumValueOfParameterNamed(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME,
LoanBuyDownFeeStrategy.class);
actualChanges.put(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, newValue);
loanRepaymentScheduleDetail.setBuyDownFeeStrategy(newValue);
}
if (command.parameterExists(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME)) {
final LoanBuyDownFeeIncomeType newValue = command
.enumValueOfParameterNamed(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, LoanBuyDownFeeIncomeType.class);
actualChanges.put(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, newValue);
loanRepaymentScheduleDetail.setBuyDownFeeIncomeType(newValue);
}
return actualChanges;
}
@@ -109,6 +109,9 @@ import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
@@ -819,6 +822,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
+ " l.capitalized_income_calculation_type as capitalizedIncomeCalculationType, "
+ " l.capitalized_income_strategy as capitalizedIncomeStrategy, "
+ " l.capitalized_income_type as capitalizedIncomeType, " //
+ " l.enable_buy_down_fee as enableBuyDownFee, " + " l.buy_down_fee_calculation_type as buyDownFeeCalculationType, "
+ " l.buy_down_fee_strategy as buyDownFeeStrategy, " + " l.buy_down_fee_income_type as buyDownFeeIncomeType, "
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement, "
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
+ " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, "
@@ -1208,6 +1213,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
.getStringEnumOptionData(LoanCapitalizedIncomeStrategy.class, rs.getString("capitalizedIncomeStrategy"));
final StringEnumOptionData capitalizedIncomeType = ApiFacingEnum.getStringEnumOptionData(LoanCapitalizedIncomeType.class,
rs.getString("capitalizedIncomeType"));
final boolean enableBuyDownFee = rs.getBoolean("enableBuyDownFee");
final StringEnumOptionData buyDownFeeCalculationType = ApiFacingEnum
.getStringEnumOptionData(LoanBuyDownFeeCalculationType.class, rs.getString("buyDownFeeCalculationType"));
final StringEnumOptionData buyDownFeeStrategy = ApiFacingEnum.getStringEnumOptionData(LoanBuyDownFeeStrategy.class,
rs.getString("buyDownFeeStrategy"));
final StringEnumOptionData buyDownFeeIncomeType = ApiFacingEnum.getStringEnumOptionData(LoanBuyDownFeeIncomeType.class,
rs.getString("buyDownFeeIncomeType"));
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
@@ -1228,7 +1240,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(),
loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(),
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType);
capitalizedIncomeCalculationType, capitalizedIncomeStrategy, capitalizedIncomeType, enableBuyDownFee,
buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType);
}
}
@@ -82,6 +82,9 @@ import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlat
import org.apache.fineract.portfolio.fund.data.FundData;
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -134,7 +137,9 @@ public class LoanProductsApiResource {
LoanProductConstants.REPAYMENT_START_DATE_TYPE, LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME,
LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME,
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME,
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME));
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME,
LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME,
LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME));
private static final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions"));
@@ -453,6 +458,12 @@ public class LoanProductsApiResource {
.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeStrategy.class);
final List<StringEnumOptionData> capitalizedIncomeTypeOptions = ApiFacingEnum
.getValuesAsStringEnumOptionDataList(LoanCapitalizedIncomeType.class);
final List<StringEnumOptionData> buyDownFeeCalculationTypeOptions = ApiFacingEnum
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeCalculationType.class);
final List<StringEnumOptionData> buyDownFeeStrategyOptions = ApiFacingEnum
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeStrategy.class);
final List<StringEnumOptionData> buyDownFeeIncomeTypeOptions = ApiFacingEnum
.getValuesAsStringEnumOptionDataList(LoanBuyDownFeeIncomeType.class);
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
@@ -466,7 +477,7 @@ public class LoanProductsApiResource {
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions,
daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions,
capitalizedIncomeTypeOptions);
capitalizedIncomeTypeOptions, buyDownFeeCalculationTypeOptions, buyDownFeeStrategyOptions, buyDownFeeIncomeTypeOptions);
}
}
@@ -49,6 +49,9 @@ import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -194,7 +197,9 @@ public final class LoanProductDataValidator {
LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME,
LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME,
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME,
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME));
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanProductConstants.CAPITALIZED_INCOME_TYPE_PARAM_NAME,
LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME,
LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME));
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
@@ -887,6 +892,8 @@ public final class LoanProductDataValidator {
validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator, accountingRuleType);
validateBuyDownFee(transactionProcessingStrategyCode, element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -1935,6 +1942,8 @@ public final class LoanProductDataValidator {
validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator, accountingRuleType);
validateBuyDownFee(transactionProcessingStrategyCode, element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
@@ -2745,6 +2754,61 @@ public final class LoanProductDataValidator {
}
}
private void validateBuyDownFee(String transactionProcessingStrategyCode, JsonElement element, DataValidatorBuilder baseDataValidator) {
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, element)) {
final String buyDownFeeCalculationType = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, element);
baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME)
.value(buyDownFeeCalculationType).isOneOfEnumValues(LoanBuyDownFeeCalculationType.class);
}
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, element)) {
final String buyDownFeeStrategy = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, element);
baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME).value(buyDownFeeStrategy)
.isOneOfEnumValues(LoanBuyDownFeeStrategy.class);
}
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, element)) {
final String buyDownFeeIncomeType = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, element);
baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME).value(buyDownFeeIncomeType)
.isOneOfEnumValues(LoanBuyDownFeeIncomeType.class);
}
if (AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY.equals(transactionProcessingStrategyCode)
&& this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, element)) {
Boolean enableBuyDownFee = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME,
element);
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME).value(enableBuyDownFee).ignoreIfNull()
.validateForBooleanValue();
if (enableBuyDownFee) {
final String buyDownFeeCalculationType = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME, element);
baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_CALCULATION_TYPE_PARAM_NAME)
.value(buyDownFeeCalculationType).isOneOfEnumValues(LoanBuyDownFeeCalculationType.class)
.cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, true);
final String buyDownFeeStrategy = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME, element);
baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_STRATEGY_PARAM_NAME).value(buyDownFeeStrategy)
.isOneOfEnumValues(LoanBuyDownFeeStrategy.class)
.cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, true);
final String buyDownFeeIncomeType = this.fromApiJsonHelper
.extractStringNamed(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME, element);
baseDataValidator.reset().parameter(LoanProductConstants.BUY_DOWN_FEE_INCOME_TYPE_PARAM_NAME).value(buyDownFeeIncomeType)
.isOneOfEnumValues(LoanBuyDownFeeIncomeType.class)
.cantBeBlankWhenParameterProvidedIs(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, true);
}
} else if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME, element)) {
Boolean enableBuyDownFee = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME,
element);
if (Boolean.TRUE.equals(enableBuyDownFee)) {
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_BUY_DOWN_FEE_PARAM_NAME).failWithCode(
"supported.only.for.progressive.loan.buyDownFee", "Buy down fee is only supported for Progressive loans");
}
}
}
private Integer defaultToZeroIfNull(Integer value) {
return value != null ? value : 0;
}
@@ -46,6 +46,9 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
import org.apache.fineract.portfolio.common.service.CommonEnumerations;
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeIncomeType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -296,7 +299,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lp.enable_income_capitalization as enableIncomeCapitalization, " //
+ "lp.capitalized_income_calculation_type as capitalizedIncomeCalculationType, " //
+ "lp.capitalized_income_strategy as capitalizedIncomeStrategy, " //
+ "lp.capitalized_income_type as capitalizedIncomeType " //
+ "lp.capitalized_income_type as capitalizedIncomeType, " //
+ "lp.enable_buy_down_fee as enableBuyDownFee, " + "lp.buy_down_fee_calculation_type as buyDownFeeCalculationType, "
+ "lp.buy_down_fee_strategy as buyDownFeeStrategy, " + "lp.buy_down_fee_income_type as buyDownFeeIncomeType "
+ " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id "
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
+ " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id "
@@ -567,6 +572,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
.getStringEnumOptionData(LoanCapitalizedIncomeStrategy.class, rs.getString("capitalizedIncomeStrategy"));
final StringEnumOptionData capitalizedIncome = ApiFacingEnum.getStringEnumOptionData(LoanCapitalizedIncomeType.class,
rs.getString("capitalizedIncomeType"));
final boolean enableBuyDownFee = rs.getBoolean("enableBuyDownFee");
final StringEnumOptionData buyDownFeeCalculationType = ApiFacingEnum
.getStringEnumOptionData(LoanBuyDownFeeCalculationType.class, rs.getString("buyDownFeeCalculationType"));
final StringEnumOptionData buyDownFeeStrategy = ApiFacingEnum.getStringEnumOptionData(LoanBuyDownFeeStrategy.class,
rs.getString("buyDownFeeStrategy"));
final StringEnumOptionData buyDownFeeIncomeType = ApiFacingEnum.getStringEnumOptionData(LoanBuyDownFeeIncomeType.class,
rs.getString("buyDownFeeIncomeType"));
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -592,7 +604,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
loanChargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate,
daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy,
capitalizedIncome);
capitalizedIncome, enableBuyDownFee, buyDownFeeCalculationType, buyDownFeeStrategy, buyDownFeeIncomeType);
}
}
@@ -203,4 +203,5 @@
<include file="parts/0182_transaction_summary_with_asset_owner_report_fix_charge_reason_and_add_buyback_intermediate.xml" relativeToChangelogFile="true" />
<include file="parts/0183_add_LoanCapitalizedIncomeTransactionCreatedBusinessEvent.xml" relativeToChangelogFile="true" />
<include file="parts/0184_add_document_event_configuration.xml" relativeToChangelogFile="true" />
<include file="parts/0185_loan_buy_down_fee.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
@@ -0,0 +1,55 @@
<?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.3.xsd">
<changeSet author="fineract" id="1">
<addColumn tableName="m_loan">
<column name="enable_buy_down_fee" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="buy_down_fee_calculation_type" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="buy_down_fee_strategy" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="buy_down_fee_income_type" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
</addColumn>
<addColumn tableName="m_product_loan">
<column name="enable_buy_down_fee" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="buy_down_fee_calculation_type" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="buy_down_fee_strategy" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="buy_down_fee_income_type" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -99,7 +99,7 @@ public class DefaultScheduledDateGeneratorTest {
DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO, null, NONE, null, ZERO,
EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false, DISBURSEMENT_DATE,
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null, false, null, false, null, null,
null);
null, false, null, null, null);
// when
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate,
@@ -180,7 +180,7 @@ public class DefaultScheduledDateGeneratorTest {
EMPTY_LIST, BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null,
null, ZERO, null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false,
null, false, DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null,
false, null, false, null, null, null);
false, null, false, null, null, null, false, null, null, null);
}
private HolidayDetailDTO createHolidayDTO() {