FINERACT-2232: Progressive loan income capitalization config
This commit is contained in:
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum LoanCapitalizedIncomeCalculationType {
|
||||
|
||||
FLAT("loanCapitalizedIncomeCalculationType.flat", "Flat");
|
||||
|
||||
private final String code;
|
||||
private final String humanReadableName;
|
||||
|
||||
public static List<StringEnumOptionData> getValuesAsStringEnumOptionDataList() {
|
||||
return Arrays.stream(values()).map(v -> new StringEnumOptionData(v.name(), v.getCode(), v.getHumanReadableName())).toList();
|
||||
}
|
||||
|
||||
public StringEnumOptionData getValueAsStringEnumOptionData() {
|
||||
return new StringEnumOptionData(name(), getCode(), getHumanReadableName());
|
||||
}
|
||||
|
||||
public static StringEnumOptionData getStringEnumOptionData(String name) {
|
||||
return name == null || name.trim().isEmpty() ? null
|
||||
: LoanCapitalizedIncomeCalculationType.valueOf(name).getValueAsStringEnumOptionData();
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.data.StringEnumOptionData;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum LoanCapitalizedIncomeStrategy {
|
||||
|
||||
EQUAL_AMORTIZATION("capitalizedIncome.strategy.equalAmortization", "Equal amortization");
|
||||
|
||||
private final String code;
|
||||
private final String humanReadableName;
|
||||
|
||||
public static List<StringEnumOptionData> getValuesAsStringEnumOptionDataList() {
|
||||
return Arrays.stream(values()).map(v -> new StringEnumOptionData(v.name(), v.getCode(), v.getHumanReadableName())).toList();
|
||||
}
|
||||
|
||||
public StringEnumOptionData getValueAsStringEnumOptionData() {
|
||||
return new StringEnumOptionData(name(), getCode(), getHumanReadableName());
|
||||
}
|
||||
|
||||
public static StringEnumOptionData getStringEnumOptionData(String name) {
|
||||
return name == null || name.trim().isEmpty() ? null : LoanCapitalizedIncomeStrategy.valueOf(name).getValueAsStringEnumOptionData();
|
||||
}
|
||||
}
|
||||
+42
-5
@@ -46,6 +46,8 @@ import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanProductRelatedDetailMinimumData;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
@@ -232,6 +234,9 @@ public final class LoanApplicationTerms {
|
||||
private LoanChargeOffBehaviour chargeOffBehaviour;
|
||||
private boolean interestRecognitionOnDisbursementDate;
|
||||
private DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
private boolean enableIncomeCapitalization;
|
||||
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
|
||||
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
|
||||
|
||||
private LoanApplicationTerms(Builder builder) {
|
||||
this.currency = builder.currency;
|
||||
@@ -268,6 +273,9 @@ public final class LoanApplicationTerms {
|
||||
} else {
|
||||
this.downPaymentAmount = Money.zero(getCurrency(), builder.mc);
|
||||
}
|
||||
this.enableIncomeCapitalization = builder.enableIncomeCapitalization;
|
||||
this.capitalizedIncomeCalculationType = builder.capitalizedIncomeCalculationType;
|
||||
this.capitalizedIncomeStrategy = builder.capitalizedIncomeStrategy;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@@ -297,6 +305,9 @@ public final class LoanApplicationTerms {
|
||||
private MathContext mc;
|
||||
private Boolean interestRecognitionOnDisbursementDate;
|
||||
private DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
private boolean enableIncomeCapitalization;
|
||||
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
|
||||
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
|
||||
|
||||
public Builder currency(CurrencyData currency) {
|
||||
this.currency = currency;
|
||||
@@ -413,6 +424,21 @@ public final class LoanApplicationTerms {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder enableIncomeCapitalization(boolean value) {
|
||||
this.enableIncomeCapitalization = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder capitalizedIncomeCalculationType(LoanCapitalizedIncomeCalculationType value) {
|
||||
this.capitalizedIncomeCalculationType = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder capitalizedIncomeStrategy(LoanCapitalizedIncomeStrategy value) {
|
||||
this.capitalizedIncomeStrategy = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanApplicationTerms build() {
|
||||
return new LoanApplicationTerms(this);
|
||||
}
|
||||
@@ -480,7 +506,9 @@ public final class LoanApplicationTerms {
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
|
||||
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
|
||||
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
@@ -500,7 +528,8 @@ public final class LoanApplicationTerms {
|
||||
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
|
||||
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -574,7 +603,9 @@ public final class LoanApplicationTerms {
|
||||
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
|
||||
loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(),
|
||||
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour(),
|
||||
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(), loanProductRelatedDetail.getDaysInYearCustomStrategy());
|
||||
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(), loanProductRelatedDetail.getDaysInYearCustomStrategy(),
|
||||
loanProductRelatedDetail.isEnableIncomeCapitalization(), loanProductRelatedDetail.getCapitalizedIncomeCalculationType(),
|
||||
loanProductRelatedDetail.getCapitalizedIncomeStrategy());
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency,
|
||||
@@ -605,7 +636,9 @@ public final class LoanApplicationTerms {
|
||||
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting,
|
||||
final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour,
|
||||
final boolean interestRecognitionOnDisbursementDate, final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
final boolean interestRecognitionOnDisbursementDate, final DaysInYearCustomStrategyType daysInYearCustomStrategy,
|
||||
final boolean enableIncomeCapitalization, final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
|
||||
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
|
||||
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
@@ -707,6 +740,9 @@ public final class LoanApplicationTerms {
|
||||
this.chargeOffBehaviour = chargeOffBehaviour;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
this.enableIncomeCapitalization = enableIncomeCapitalization;
|
||||
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
|
||||
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1568,7 +1604,8 @@ public final class LoanApplicationTerms {
|
||||
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
|
||||
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
|
||||
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes,
|
||||
this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy);
|
||||
this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy,
|
||||
this.enableIncomeCapitalization, this.capitalizedIncomeCalculationType, this.capitalizedIncomeStrategy);
|
||||
}
|
||||
|
||||
public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
|
||||
|
||||
+5
@@ -171,4 +171,9 @@ public interface LoanProductConstants {
|
||||
String CHARGE_OFF_BEHAVIOUR = "chargeOffBehaviour";
|
||||
|
||||
String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate";
|
||||
|
||||
// Capitalized income
|
||||
String ENABLE_INCOME_CAPITALIZATION_PARAM_NAME = "enableIncomeCapitalization";
|
||||
String CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME = "capitalizedIncomeCalculationType";
|
||||
String CAPITALIZED_INCOME_STRATEGY_PARAM_NAME = "capitalizedIncomeStrategy";
|
||||
}
|
||||
|
||||
+34
@@ -195,6 +195,12 @@ public final class LoanProductsApiResourceSwagger {
|
||||
public Boolean enableAccrualActivityPosting;
|
||||
@Schema(example = "false")
|
||||
public Boolean interestRecognitionOnDisbursementDate;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT", allowableValues = "FLAT")
|
||||
public String capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
|
||||
public String capitalizedIncomeStrategy;
|
||||
|
||||
// Interest Recalculation
|
||||
@Schema(example = "false")
|
||||
@@ -651,6 +657,12 @@ public final class LoanProductsApiResourceSwagger {
|
||||
public StringEnumOptionData chargeOffBehaviour;
|
||||
@Schema(example = "false")
|
||||
public Boolean interestRecognitionOnDisbursementDate;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT")
|
||||
public StringEnumOptionData capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION")
|
||||
public StringEnumOptionData capitalizedIncomeStrategy;
|
||||
}
|
||||
|
||||
@Schema(description = "GetLoanProductsTemplateResponse")
|
||||
@@ -1097,6 +1109,14 @@ public final class LoanProductsApiResourceSwagger {
|
||||
public List<GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions;
|
||||
public StringEnumOptionData chargeOffBehaviour;
|
||||
public List<StringEnumOptionData> chargeOffBehaviourOptions;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT")
|
||||
public StringEnumOptionData capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION")
|
||||
public StringEnumOptionData capitalizedIncomeStrategy;
|
||||
public List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
|
||||
public List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
|
||||
}
|
||||
|
||||
@Schema(description = "GetLoanProductsProductIdResponse")
|
||||
@@ -1416,6 +1436,14 @@ public final class LoanProductsApiResourceSwagger {
|
||||
public StringEnumOptionData chargeOffBehaviour;
|
||||
@Schema(example = "false")
|
||||
public Boolean interestRecognitionOnDisbursementDate;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT")
|
||||
public StringEnumOptionData capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION")
|
||||
public StringEnumOptionData capitalizedIncomeStrategy;
|
||||
public List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
|
||||
public List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
|
||||
}
|
||||
|
||||
@Schema(description = "PutLoanProductsProductIdRequest")
|
||||
@@ -1669,6 +1697,12 @@ public final class LoanProductsApiResourceSwagger {
|
||||
public List<String> supportedInterestRefundTypes;
|
||||
@Schema(example = "REGULAR")
|
||||
public String chargeOffBehaviour;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT", allowableValues = "FLAT")
|
||||
public String capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
|
||||
public String capitalizedIncomeStrategy;
|
||||
}
|
||||
|
||||
public static final class AdvancedPaymentData {
|
||||
|
||||
+42
-6
@@ -48,6 +48,8 @@ import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.apache.fineract.portfolio.fund.data.FundData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -238,6 +240,11 @@ public class LoanProductData implements Serializable {
|
||||
private final boolean interestRecognitionOnDisbursementDate;
|
||||
private final List<StringEnumOptionData> daysInYearCustomStrategyOptions;
|
||||
private final StringEnumOptionData daysInYearCustomStrategy;
|
||||
private Boolean enableIncomeCapitalization;
|
||||
private StringEnumOptionData capitalizedIncomeCalculationType;
|
||||
private StringEnumOptionData capitalizedIncomeStrategy;
|
||||
private List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
|
||||
private List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
|
||||
|
||||
/**
|
||||
* Used when returning lookup information about loan product for dropdowns.
|
||||
@@ -342,6 +349,9 @@ public class LoanProductData implements Serializable {
|
||||
final StringEnumOptionData chargeOffBehaviour = null;
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
final boolean enableIncomeCapitalization = false;
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType = null;
|
||||
final StringEnumOptionData capitalizedIncomeStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -363,7 +373,8 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -468,6 +479,9 @@ public class LoanProductData implements Serializable {
|
||||
final StringEnumOptionData chargeOffBehaviour = null;
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
final boolean enableIncomeCapitalization = false;
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType = null;
|
||||
final StringEnumOptionData capitalizedIncomeStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -489,7 +503,8 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -601,6 +616,9 @@ public class LoanProductData implements Serializable {
|
||||
final StringEnumOptionData chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR.getValueAsStringEnumOptionData();
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
final boolean enableIncomeCapitalization = false;
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType = null;
|
||||
final StringEnumOptionData capitalizedIncomeStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -622,7 +640,8 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -728,6 +747,9 @@ public class LoanProductData implements Serializable {
|
||||
final StringEnumOptionData chargeOffBehaviour = null;
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
final boolean enableIncomeCapitalization = false;
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType = null;
|
||||
final StringEnumOptionData capitalizedIncomeStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -749,7 +771,8 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
}
|
||||
|
||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||
@@ -804,7 +827,8 @@ public class LoanProductData implements Serializable {
|
||||
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes,
|
||||
StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final StringEnumOptionData daysInYearCustomStrategy) {
|
||||
final StringEnumOptionData daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType, final StringEnumOptionData capitalizedIncomeStrategy) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -861,6 +885,9 @@ public class LoanProductData implements Serializable {
|
||||
this.rates = rates;
|
||||
this.isRatesEnabled = isRatesEnabled;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
this.enableIncomeCapitalization = enableIncomeCapitalization;
|
||||
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
|
||||
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
|
||||
|
||||
this.chargeOptions = null;
|
||||
this.penaltyOptions = null;
|
||||
@@ -950,6 +977,8 @@ public class LoanProductData implements Serializable {
|
||||
this.chargeOffReasonOptions = null;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList();
|
||||
this.capitalizedIncomeCalculationTypeOptions = LoanCapitalizedIncomeCalculationType.getValuesAsStringEnumOptionDataList();
|
||||
this.capitalizedIncomeStrategyOptions = LoanCapitalizedIncomeStrategy.getValuesAsStringEnumOptionDataList();
|
||||
}
|
||||
|
||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||
@@ -974,7 +1003,9 @@ public class LoanProductData implements Serializable {
|
||||
final List<EnumOptionData> creditAllocationAllocationTypes,
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypesOptions,
|
||||
final List<StringEnumOptionData> chargeOffBehaviourOptions, final List<CodeValueData> chargeOffReasonOptions,
|
||||
final List<StringEnumOptionData> daysInYearCustomStrategyOptions) {
|
||||
final List<StringEnumOptionData> daysInYearCustomStrategyOptions,
|
||||
final List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions,
|
||||
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions) {
|
||||
|
||||
this.id = productData.id;
|
||||
this.name = productData.name;
|
||||
@@ -1138,6 +1169,11 @@ public class LoanProductData implements Serializable {
|
||||
this.interestRecognitionOnDisbursementDate = productData.interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategyOptions = daysInYearCustomStrategyOptions;
|
||||
this.daysInYearCustomStrategy = productData.daysInYearCustomStrategy;
|
||||
this.enableIncomeCapitalization = productData.enableIncomeCapitalization;
|
||||
this.capitalizedIncomeCalculationType = productData.capitalizedIncomeCalculationType;
|
||||
this.capitalizedIncomeStrategy = productData.capitalizedIncomeStrategy;
|
||||
this.capitalizedIncomeCalculationTypeOptions = capitalizedIncomeCalculationTypeOptions;
|
||||
this.capitalizedIncomeStrategyOptions = capitalizedIncomeStrategyOptions;
|
||||
}
|
||||
|
||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||
|
||||
+7
-2
@@ -59,6 +59,8 @@ import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -280,7 +282,9 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
|
||||
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
|
||||
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
||||
|
||||
@@ -330,7 +334,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
|
||||
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
|
||||
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy,
|
||||
enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
|
||||
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
|
||||
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
|
||||
|
||||
+24
-3
@@ -36,6 +36,8 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -168,6 +170,17 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
@Column(name = "days_in_year_custom_strategy")
|
||||
private DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
|
||||
@Column(name = "enable_income_capitalization")
|
||||
private boolean enableIncomeCapitalization = false;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "capitalized_income_calculation_type")
|
||||
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "capitalized_income_strategy")
|
||||
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
|
||||
|
||||
public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal,
|
||||
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
|
||||
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
|
||||
@@ -182,7 +195,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
|
||||
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
|
||||
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
|
||||
|
||||
final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData);
|
||||
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
||||
@@ -192,7 +207,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
|
||||
loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
|
||||
chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
}
|
||||
|
||||
protected LoanProductRelatedDetail() {
|
||||
@@ -213,7 +229,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
|
||||
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType,
|
||||
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy) {
|
||||
this.currency = currency;
|
||||
this.principal = defaultPrincipal;
|
||||
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
||||
@@ -251,6 +269,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.chargeOffBehaviour = chargeOffBehaviour;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
this.enableIncomeCapitalization = enableIncomeCapitalization;
|
||||
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
|
||||
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
|
||||
}
|
||||
|
||||
private Integer defaultToNullIfZero(final Integer value) {
|
||||
|
||||
+5
-1
@@ -132,6 +132,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
@@ -1135,7 +1137,9 @@ public class LoansApiResource {
|
||||
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
|
||||
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled, collectionData,
|
||||
LoanScheduleType.getValuesAsEnumOptionDataList(), LoanScheduleProcessingType.getValuesAsEnumOptionDataList(),
|
||||
loanTermVariations, DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList());
|
||||
loanTermVariations, DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList(),
|
||||
LoanCapitalizedIncomeCalculationType.getValuesAsStringEnumOptionDataList(),
|
||||
LoanCapitalizedIncomeStrategy.getValuesAsStringEnumOptionDataList());
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
|
||||
+12
@@ -1206,6 +1206,12 @@ final class LoansApiResourceSwagger {
|
||||
public StringEnumOptionData chargeOffBehaviour;
|
||||
@Schema(example = "false")
|
||||
public Boolean interestRecognitionOnDisbursementDate;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT")
|
||||
public StringEnumOptionData capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION")
|
||||
public StringEnumOptionData capitalizedIncomeStrategy;
|
||||
}
|
||||
|
||||
@Schema(description = "GetLoansResponse")
|
||||
@@ -1309,6 +1315,12 @@ final class LoansApiResourceSwagger {
|
||||
public BigDecimal fixedEmiAmount;
|
||||
@Schema(example = "false")
|
||||
public Boolean interestRecognitionOnDisbursementDate;
|
||||
@Schema(example = "false")
|
||||
public Boolean enableIncomeCapitalization;
|
||||
@Schema(example = "FLAT", allowableValues = "FLAT")
|
||||
public String capitalizedIncomeCalculationType;
|
||||
@Schema(example = "EQUAL_AMORTIZATION", allowableValues = "EQUAL_AMORTIZATION")
|
||||
public String capitalizedIncomeStrategy;
|
||||
|
||||
public List<PostLoansRequestChargeData> charges;
|
||||
|
||||
|
||||
+17
-4
@@ -172,6 +172,8 @@ public class LoanAccountData {
|
||||
private List<EnumOptionData> loanScheduleTypeOptions;
|
||||
private List<EnumOptionData> loanScheduleProcessingTypeOptions;
|
||||
private List<StringEnumOptionData> daysInYearCustomStrategyOptions;
|
||||
private List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
|
||||
private List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
|
||||
|
||||
@Transient
|
||||
private BigDecimal feeChargesAtDisbursementCharged;
|
||||
@@ -274,6 +276,9 @@ public class LoanAccountData {
|
||||
private EnumOptionData loanScheduleProcessingType;
|
||||
|
||||
private StringEnumOptionData chargeOffBehaviour;
|
||||
private Boolean enableIncomeCapitalization;
|
||||
private StringEnumOptionData capitalizedIncomeCalculationType;
|
||||
private StringEnumOptionData capitalizedIncomeStrategy;
|
||||
|
||||
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
|
||||
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
|
||||
@@ -464,7 +469,9 @@ 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 isInterestRecognitionOnDisbursementDate, final StringEnumOptionData daysInYearCustomStrategy,
|
||||
final boolean enableIncomeCapitalization, final StringEnumOptionData capitalizedIncomeCalculationType,
|
||||
final StringEnumOptionData capitalizedIncomeStrategy) {
|
||||
|
||||
final CollectionData delinquent = CollectionData.template();
|
||||
|
||||
@@ -510,7 +517,9 @@ public class LoanAccountData {
|
||||
.setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency).setLoanScheduleType(loanScheduleType)
|
||||
.setLoanScheduleProcessingType(loanScheduleProcessingType).setFixedLength(fixedLength)
|
||||
.setChargeOffBehaviour(chargeOffBehaviour).setInterestRecognitionOnDisbursementDate(isInterestRecognitionOnDisbursementDate)
|
||||
.setDaysInYearCustomStrategy(daysInYearCustomStrategy);
|
||||
.setDaysInYearCustomStrategy(daysInYearCustomStrategy).setEnableIncomeCapitalization(enableIncomeCapitalization)
|
||||
.setCapitalizedIncomeCalculationType(capitalizedIncomeCalculationType)
|
||||
.setCapitalizedIncomeStrategy(capitalizedIncomeStrategy);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -536,7 +545,9 @@ public class LoanAccountData {
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final List<RateData> rates, final Boolean isRatesEnabled,
|
||||
final CollectionData delinquent, final List<EnumOptionData> loanScheduleTypeOptions,
|
||||
final List<EnumOptionData> loanScheduleProcessingTypeOptions, final List<LoanTermVariationsData> loanTermVariations,
|
||||
final List<StringEnumOptionData> daysInYearCustomStrategyOptions) {
|
||||
final List<StringEnumOptionData> daysInYearCustomStrategyOptions,
|
||||
final List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions,
|
||||
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions) {
|
||||
|
||||
// TODO: why are these variables 'calendarData', 'chargeTemplate' never used (see original private constructor)
|
||||
|
||||
@@ -557,7 +568,9 @@ public class LoanAccountData {
|
||||
.setClientActiveLoanOptions(clientActiveLoanOptions).setRates(rates).setIsRatesEnabled(isRatesEnabled)
|
||||
.setDelinquent(delinquent).setLoanScheduleTypeOptions(loanScheduleTypeOptions)
|
||||
.setLoanScheduleProcessingTypeOptions(loanScheduleProcessingTypeOptions).setLoanTermVariations(loanTermVariations)
|
||||
.setDaysInYearCustomStrategyOptions(daysInYearCustomStrategyOptions);
|
||||
.setDaysInYearCustomStrategyOptions(daysInYearCustomStrategyOptions)
|
||||
.setCapitalizedIncomeCalculationTypeOptions(capitalizedIncomeCalculationTypeOptions)
|
||||
.setCapitalizedIncomeStrategyOptions(capitalizedIncomeStrategyOptions);
|
||||
}
|
||||
|
||||
public LoanAccountData associationsAndTemplate(final Collection<LoanProductData> productOptions,
|
||||
|
||||
+4
-1
@@ -544,7 +544,10 @@ public class LoanScheduleAssembler {
|
||||
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting(),
|
||||
loanProduct.getLoanProductRelatedDetail().getSupportedInterestRefundTypes(),
|
||||
loanProduct.getLoanProductRelatedDetail().getChargeOffBehaviour(), interestRecognitionOnDisbursementDate,
|
||||
loanProduct.getLoanProductRelatedDetail().getDaysInYearCustomStrategy());
|
||||
loanProduct.getLoanProductRelatedDetail().getDaysInYearCustomStrategy(),
|
||||
loanProduct.getLoanProductRelatedDetail().isEnableIncomeCapitalization(),
|
||||
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeCalculationType(),
|
||||
loanProduct.getLoanProductRelatedDetail().getCapitalizedIncomeStrategy());
|
||||
}
|
||||
|
||||
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
||||
|
||||
+10
-1
@@ -40,6 +40,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
@@ -305,6 +307,13 @@ public class LoanProductAssembler {
|
||||
chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR;
|
||||
}
|
||||
|
||||
final boolean enableIncomeCapitalization = command
|
||||
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME);
|
||||
final LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType = command.enumValueOfParameterNamed(
|
||||
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, LoanCapitalizedIncomeCalculationType.class);
|
||||
final LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy = command.enumValueOfParameterNamed(
|
||||
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanCapitalizedIncomeStrategy.class);
|
||||
|
||||
return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, loanProductCreditAllocationRules,
|
||||
name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod,
|
||||
minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod,
|
||||
@@ -325,7 +334,7 @@ public class LoanProductAssembler {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
|
||||
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate,
|
||||
daysInYearCustomStrategy);
|
||||
daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -26,6 +26,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -296,6 +298,28 @@ public class LoanProductRelatedDetailUpdateUtil {
|
||||
loanRepaymentScheduleDetail.updateInterestRecognitionOnDisbursementDate(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME,
|
||||
loanRepaymentScheduleDetail.isEnableIncomeCapitalization())) {
|
||||
final boolean newValue = command
|
||||
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME);
|
||||
actualChanges.put(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, newValue);
|
||||
loanRepaymentScheduleDetail.setEnableIncomeCapitalization(newValue);
|
||||
}
|
||||
|
||||
if (command.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME)) {
|
||||
final LoanCapitalizedIncomeCalculationType newValue = command.enumValueOfParameterNamed(
|
||||
LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, LoanCapitalizedIncomeCalculationType.class);
|
||||
actualChanges.put(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, newValue);
|
||||
loanRepaymentScheduleDetail.setCapitalizedIncomeCalculationType(newValue);
|
||||
}
|
||||
|
||||
if (command.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME)) {
|
||||
final LoanCapitalizedIncomeStrategy newValue = command.enumValueOfParameterNamed(
|
||||
LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, LoanCapitalizedIncomeStrategy.class);
|
||||
actualChanges.put(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, newValue);
|
||||
loanRepaymentScheduleDetail.setCapitalizedIncomeStrategy(newValue);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -106,6 +106,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
@@ -722,6 +724,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
+ " l.is_floating_interest_rate as isFloatingInterestRate, "
|
||||
+ " l.interest_rate_differential as interestRateDifferential, "
|
||||
+ " l.days_in_year_custom_strategy as daysInYearCustomStrategy, "
|
||||
+ " l.enable_income_capitalization as enableIncomeCapitalization, "
|
||||
+ " l.capitalized_income_calculation_type as capitalizedIncomeCalculationType, "
|
||||
+ " l.capitalized_income_strategy as capitalizedIncomeStrategy, "
|
||||
+ " 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, "
|
||||
@@ -1098,6 +1103,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
final boolean interestRecognitionOnDisbursementDate = rs.getBoolean("interestRecognitionOnDisbursementDate");
|
||||
final StringEnumOptionData daysInYearCustomStrategy = DaysInYearCustomStrategyType
|
||||
.getStringEnumOptionData(rs.getString("daysInYearCustomStrategy"));
|
||||
final boolean enableIncomeCapitalization = rs.getBoolean("enableIncomeCapitalization");
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType = LoanCapitalizedIncomeCalculationType
|
||||
.getStringEnumOptionData(rs.getString("capitalizedIncomeCalculationType"));
|
||||
final StringEnumOptionData capitalizedIncomeStrategy = LoanCapitalizedIncomeStrategy
|
||||
.getStringEnumOptionData(rs.getString("capitalizedIncomeStrategy"));
|
||||
|
||||
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
|
||||
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
|
||||
@@ -1117,7 +1127,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
|
||||
lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(),
|
||||
loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(),
|
||||
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
|
||||
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -81,6 +81,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -127,7 +129,10 @@ public class LoanProductsApiResource {
|
||||
LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT,
|
||||
LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.ENABLE_DOWN_PAYMENT,
|
||||
LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT,
|
||||
LoanProductConstants.REPAYMENT_START_DATE_TYPE, LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME));
|
||||
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));
|
||||
|
||||
private static final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions"));
|
||||
@@ -439,6 +444,10 @@ public class LoanProductsApiResource {
|
||||
.retrieveCodeValuesByCode(LoanApiConstants.CHARGE_OFF_REASONS);
|
||||
final List<StringEnumOptionData> daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType
|
||||
.getValuesAsStringEnumOptionDataList();
|
||||
final List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions = LoanCapitalizedIncomeCalculationType
|
||||
.getValuesAsStringEnumOptionDataList();
|
||||
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions = LoanCapitalizedIncomeStrategy
|
||||
.getValuesAsStringEnumOptionDataList();
|
||||
|
||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
|
||||
@@ -451,7 +460,7 @@ public class LoanProductsApiResource {
|
||||
advancedPaymentAllocationTypes, LoanScheduleType.getValuesAsEnumOptionDataList(),
|
||||
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
|
||||
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions,
|
||||
daysInYearCustomStrategyOptions);
|
||||
daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+44
-1
@@ -49,6 +49,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
@@ -186,7 +188,10 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
|
||||
LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES,
|
||||
LoanProductConstants.CHARGE_OFF_BEHAVIOUR, LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE,
|
||||
LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME));
|
||||
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));
|
||||
|
||||
private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
|
||||
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
|
||||
@@ -877,6 +882,8 @@ public final class LoanProductDataValidator {
|
||||
"Charge off behaviour is only supported for Progressive loans");
|
||||
}
|
||||
|
||||
validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -1922,6 +1929,9 @@ public final class LoanProductDataValidator {
|
||||
|
||||
validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
|
||||
|
||||
validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -2660,6 +2670,39 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateIncomeCapitalization(String transactionProcessingStrategyCode, JsonElement element,
|
||||
DataValidatorBuilder baseDataValidator) {
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, element)) {
|
||||
final String capitalizedIncomeCalculationType = this.fromApiJsonHelper
|
||||
.extractStringNamed(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.CAPITALIZED_INCOME_CALCULATION_TYPE_PARAM_NAME)
|
||||
.value(capitalizedIncomeCalculationType).isOneOfEnumValues(LoanCapitalizedIncomeCalculationType.class);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, element)) {
|
||||
final String capitalizedIncomeStrategy = this.fromApiJsonHelper
|
||||
.extractStringNamed(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.CAPITALIZED_INCOME_STRATEGY_PARAM_NAME)
|
||||
.value(capitalizedIncomeStrategy).isOneOfEnumValues(LoanCapitalizedIncomeStrategy.class);
|
||||
}
|
||||
|
||||
if (AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY.equals(transactionProcessingStrategyCode)
|
||||
&& this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, element)) {
|
||||
Boolean enableIncomeCapitalization = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME)
|
||||
.value(enableIncomeCapitalization).ignoreIfNull().validateForBooleanValue();
|
||||
} else if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, element)) {
|
||||
Boolean enableIncomeCapitalization = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME, element);
|
||||
if (Boolean.TRUE.equals(enableIncomeCapitalization)) {
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INCOME_CAPITALIZATION_PARAM_NAME).failWithCode(
|
||||
"supported.only.for.progressive.loan.income.capitalization",
|
||||
"Income capitalization is only supported for Progressive loans");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Integer defaultToZeroIfNull(Integer value) {
|
||||
return value != null ? value : 0;
|
||||
}
|
||||
|
||||
+13
-3
@@ -45,6 +45,8 @@ 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.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeOffBehaviour;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
@@ -288,8 +290,11 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, " + "lvi.minimum_gap as minimumGap, "
|
||||
+ "lvi.maximum_gap as maximumGap, dbuc.id as delinquencyBucketId, dbuc.name as delinquencyBucketName, "
|
||||
+ "lp.can_use_for_topup as canUseForTopup, lp.is_equal_amortization as isEqualAmortization, lp.loan_schedule_type as loanScheduleType, lp.loan_schedule_processing_type as loanScheduleProcessingType, lp.supported_interest_refund_types as supportedInterestRefundTypes, "
|
||||
+ "lp.charge_off_behaviour as chargeOffBehaviour" + " from m_product_loan lp "
|
||||
+ " left join m_fund f on f.id = lp.fund_id "
|
||||
+ "lp.charge_off_behaviour as chargeOffBehaviour, " //
|
||||
+ "lp.enable_income_capitalization as enableIncomeCapitalization, " //
|
||||
+ "lp.capitalized_income_calculation_type as capitalizedIncomeCalculationType, " //
|
||||
+ "lp.capitalized_income_strategy as capitalizedIncomeStrategy " //
|
||||
+ " 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 "
|
||||
+ " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id "
|
||||
@@ -552,6 +557,11 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final String chargeOffBehaviourStr = rs.getString("chargeOffBehaviour");
|
||||
final LoanChargeOffBehaviour loanChargeOffBehaviour = LoanChargeOffBehaviour.valueOf(chargeOffBehaviourStr);
|
||||
final boolean interestRecognitionOnDisbursementDate = rs.getBoolean("interestRecognitionOnDisbursementDate");
|
||||
final boolean enableIncomeCapitalization = rs.getBoolean("enableIncomeCapitalization");
|
||||
final StringEnumOptionData capitalizedIncomeCalculationType = LoanCapitalizedIncomeCalculationType
|
||||
.getStringEnumOptionData(rs.getString("capitalizedIncomeCalculationType"));
|
||||
final StringEnumOptionData capitalizedIncomeStrategy = LoanCapitalizedIncomeStrategy
|
||||
.getStringEnumOptionData(rs.getString("capitalizedIncomeStrategy"));
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -576,7 +586,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
|
||||
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
|
||||
loanChargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate,
|
||||
daysInYearCustomStrategy);
|
||||
daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,4 +189,5 @@
|
||||
<include file="parts/0168_transaction_summary_with_asset_owner_report_add_active_intermediate_filtering.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0169_add_missing_permissions.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0170_days_in_year_custom_strategy.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0171_loan_capitalized_income.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?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_income_capitalization" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="capitalized_income_calculation_type" type="VARCHAR(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="capitalized_income_strategy" type="VARCHAR(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_product_loan">
|
||||
<column name="enable_income_capitalization" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="capitalized_income_calculation_type" type="VARCHAR(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="capitalized_income_strategy" type="VARCHAR(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+3
-2
@@ -98,7 +98,8 @@ public class DefaultScheduledDateGeneratorTest {
|
||||
Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, 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);
|
||||
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null, false, null, false, null,
|
||||
null);
|
||||
|
||||
// when
|
||||
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate,
|
||||
@@ -179,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, false, null, null);
|
||||
}
|
||||
|
||||
private HolidayDetailDTO createHolidayDTO() {
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
|
||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||
import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LoanProductTest extends BaseLoanIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testIncomeCapitalizationEnabled() {
|
||||
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||
|
||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
|
||||
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
|
||||
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION));
|
||||
|
||||
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
|
||||
.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||
Assertions.assertEquals(Boolean.TRUE, loanProductsProductIdResponse.getEnableIncomeCapitalization());
|
||||
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeCalculationType());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeCalculationType.FLAT.getCode(),
|
||||
loanProductsProductIdResponse.getCapitalizedIncomeCalculationType().getCode());
|
||||
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeStrategy());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
|
||||
loanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
|
||||
|
||||
runAt("20 December 2024", () -> {
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
|
||||
430.0, 7.0, 6, null);
|
||||
|
||||
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
Assertions.assertEquals(Boolean.TRUE, loanDetails.getEnableIncomeCapitalization());
|
||||
Assertions.assertNotNull(loanDetails.getCapitalizedIncomeCalculationType());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeCalculationType.FLAT.getCode(),
|
||||
loanDetails.getCapitalizedIncomeCalculationType().getCode());
|
||||
Assertions.assertNotNull(loanDetails.getCapitalizedIncomeStrategy());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
|
||||
loanDetails.getCapitalizedIncomeStrategy().getCode());
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> disburseLoan(loanId, BigDecimal.valueOf(430), "20 December 2024"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncomeCapitalizationDisabled() {
|
||||
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||
|
||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(false)
|
||||
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
|
||||
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION));
|
||||
|
||||
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
|
||||
.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||
Assertions.assertEquals(Boolean.FALSE, loanProductsProductIdResponse.getEnableIncomeCapitalization());
|
||||
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeCalculationType());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeCalculationType.FLAT.getCode(),
|
||||
loanProductsProductIdResponse.getCapitalizedIncomeCalculationType().getCode());
|
||||
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeStrategy());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
|
||||
loanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
|
||||
|
||||
runAt("20 December 2024", () -> {
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024",
|
||||
430.0, 7.0, 6, null);
|
||||
|
||||
final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
Assertions.assertEquals(Boolean.FALSE, loanDetails.getEnableIncomeCapitalization());
|
||||
Assertions.assertNotNull(loanDetails.getCapitalizedIncomeCalculationType());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeCalculationType.FLAT.getCode(),
|
||||
loanDetails.getCapitalizedIncomeCalculationType().getCode());
|
||||
Assertions.assertNotNull(loanDetails.getCapitalizedIncomeStrategy());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
|
||||
loanDetails.getCapitalizedIncomeStrategy().getCode());
|
||||
|
||||
Assertions.assertDoesNotThrow(() -> disburseLoan(loanId, BigDecimal.valueOf(430), "20 December 2024"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncomeCapitalizationUpdateProduct() {
|
||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
|
||||
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
|
||||
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
|
||||
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION));
|
||||
|
||||
final GetLoanProductsProductIdResponse loanProductsProductIdResponse = loanProductHelper
|
||||
.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||
Assertions.assertEquals(Boolean.TRUE, loanProductsProductIdResponse.getEnableIncomeCapitalization());
|
||||
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeCalculationType());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeCalculationType.FLAT.getCode(),
|
||||
loanProductsProductIdResponse.getCapitalizedIncomeCalculationType().getCode());
|
||||
Assertions.assertNotNull(loanProductsProductIdResponse.getCapitalizedIncomeStrategy());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
|
||||
loanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
|
||||
|
||||
loanProductHelper.updateLoanProductById(loanProductsResponse.getResourceId(),
|
||||
new PutLoanProductsProductIdRequest().enableIncomeCapitalization(false));
|
||||
|
||||
final GetLoanProductsProductIdResponse updatedLoanProductsProductIdResponse = loanProductHelper
|
||||
.retrieveLoanProductById(loanProductsResponse.getResourceId());
|
||||
Assertions.assertEquals(Boolean.FALSE, updatedLoanProductsProductIdResponse.getEnableIncomeCapitalization());
|
||||
Assertions.assertNotNull(updatedLoanProductsProductIdResponse.getCapitalizedIncomeCalculationType());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeCalculationType.FLAT.getCode(),
|
||||
updatedLoanProductsProductIdResponse.getCapitalizedIncomeCalculationType().getCode());
|
||||
Assertions.assertNotNull(updatedLoanProductsProductIdResponse.getCapitalizedIncomeStrategy());
|
||||
Assertions.assertEquals(LoanCapitalizedIncomeStrategy.EQUAL_AMORTIZATION.getCode(),
|
||||
updatedLoanProductsProductIdResponse.getCapitalizedIncomeStrategy().getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncomeCapitalizationCumulativeNotSupported() {
|
||||
Assertions.assertThrows(RuntimeException.class,
|
||||
() -> loanProductHelper.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(7.0).enableIncomeCapitalization(true)
|
||||
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
|
||||
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user