FINERACT-2232: Progressive loan income capitalization config

This commit is contained in:
adam.magyari
2025-04-15 19:45:25 +02:00
committed by Adam Saghy
parent 6a65bc6592
commit 7df7be2b16
22 changed files with 601 additions and 32 deletions
@@ -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();
}
}
@@ -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();
}
}
@@ -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.HolidayDetailDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper; 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.loanaccount.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanproduct.data.LoanProductRelatedDetailMinimumData; import org.apache.fineract.portfolio.loanproduct.data.LoanProductRelatedDetailMinimumData;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod; import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
@@ -232,6 +234,9 @@ public final class LoanApplicationTerms {
private LoanChargeOffBehaviour chargeOffBehaviour; private LoanChargeOffBehaviour chargeOffBehaviour;
private boolean interestRecognitionOnDisbursementDate; private boolean interestRecognitionOnDisbursementDate;
private DaysInYearCustomStrategyType daysInYearCustomStrategy; private DaysInYearCustomStrategyType daysInYearCustomStrategy;
private boolean enableIncomeCapitalization;
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
private LoanApplicationTerms(Builder builder) { private LoanApplicationTerms(Builder builder) {
this.currency = builder.currency; this.currency = builder.currency;
@@ -268,6 +273,9 @@ public final class LoanApplicationTerms {
} else { } else {
this.downPaymentAmount = Money.zero(getCurrency(), builder.mc); this.downPaymentAmount = Money.zero(getCurrency(), builder.mc);
} }
this.enableIncomeCapitalization = builder.enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = builder.capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = builder.capitalizedIncomeStrategy;
} }
public static class Builder { public static class Builder {
@@ -297,6 +305,9 @@ public final class LoanApplicationTerms {
private MathContext mc; private MathContext mc;
private Boolean interestRecognitionOnDisbursementDate; private Boolean interestRecognitionOnDisbursementDate;
private DaysInYearCustomStrategyType daysInYearCustomStrategy; private DaysInYearCustomStrategyType daysInYearCustomStrategy;
private boolean enableIncomeCapitalization;
private LoanCapitalizedIncomeCalculationType capitalizedIncomeCalculationType;
private LoanCapitalizedIncomeStrategy capitalizedIncomeStrategy;
public Builder currency(CurrencyData currency) { public Builder currency(CurrencyData currency) {
this.currency = currency; this.currency = currency;
@@ -413,6 +424,21 @@ public final class LoanApplicationTerms {
return this; 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() { public LoanApplicationTerms build() {
return new LoanApplicationTerms(this); return new LoanApplicationTerms(this);
} }
@@ -480,7 +506,9 @@ public final class LoanApplicationTerms {
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate, 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 LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null; final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
@@ -500,7 +528,8 @@ public final class LoanApplicationTerms {
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment, isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy); interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
} }
@@ -574,7 +603,9 @@ public final class LoanApplicationTerms {
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(), loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(),
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour(), 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, 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 RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting,
final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour, 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.currency = currency;
this.loanTermFrequency = loanTermFrequency; this.loanTermFrequency = loanTermFrequency;
@@ -707,6 +740,9 @@ public final class LoanApplicationTerms {
this.chargeOffBehaviour = chargeOffBehaviour; this.chargeOffBehaviour = chargeOffBehaviour;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate; this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
this.daysInYearCustomStrategy = daysInYearCustomStrategy; this.daysInYearCustomStrategy = daysInYearCustomStrategy;
this.enableIncomeCapitalization = enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
} }
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate, public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -1568,7 +1604,8 @@ public final class LoanApplicationTerms {
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled, this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType, this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes, 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() { public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
@@ -171,4 +171,9 @@ public interface LoanProductConstants {
String CHARGE_OFF_BEHAVIOUR = "chargeOffBehaviour"; String CHARGE_OFF_BEHAVIOUR = "chargeOffBehaviour";
String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate"; 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";
} }
@@ -195,6 +195,12 @@ public final class LoanProductsApiResourceSwagger {
public Boolean enableAccrualActivityPosting; public Boolean enableAccrualActivityPosting;
@Schema(example = "false") @Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate; 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 // Interest Recalculation
@Schema(example = "false") @Schema(example = "false")
@@ -651,6 +657,12 @@ public final class LoanProductsApiResourceSwagger {
public StringEnumOptionData chargeOffBehaviour; public StringEnumOptionData chargeOffBehaviour;
@Schema(example = "false") @Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate; 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") @Schema(description = "GetLoanProductsTemplateResponse")
@@ -1097,6 +1109,14 @@ public final class LoanProductsApiResourceSwagger {
public List<GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions; public List<GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions;
public StringEnumOptionData chargeOffBehaviour; public StringEnumOptionData chargeOffBehaviour;
public List<StringEnumOptionData> chargeOffBehaviourOptions; 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") @Schema(description = "GetLoanProductsProductIdResponse")
@@ -1416,6 +1436,14 @@ public final class LoanProductsApiResourceSwagger {
public StringEnumOptionData chargeOffBehaviour; public StringEnumOptionData chargeOffBehaviour;
@Schema(example = "false") @Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate; 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") @Schema(description = "PutLoanProductsProductIdRequest")
@@ -1669,6 +1697,12 @@ public final class LoanProductsApiResourceSwagger {
public List<String> supportedInterestRefundTypes; public List<String> supportedInterestRefundTypes;
@Schema(example = "REGULAR") @Schema(example = "REGULAR")
public String chargeOffBehaviour; 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 { public static final class AdvancedPaymentData {
@@ -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.floatingrates.data.FloatingRateData;
import org.apache.fineract.portfolio.fund.data.FundData; import org.apache.fineract.portfolio.fund.data.FundData;
import org.apache.fineract.portfolio.loanaccount.data.LoanInterestRecalculationData; 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.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -238,6 +240,11 @@ public class LoanProductData implements Serializable {
private final boolean interestRecognitionOnDisbursementDate; private final boolean interestRecognitionOnDisbursementDate;
private final List<StringEnumOptionData> daysInYearCustomStrategyOptions; private final List<StringEnumOptionData> daysInYearCustomStrategyOptions;
private final StringEnumOptionData daysInYearCustomStrategy; 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. * Used when returning lookup information about loan product for dropdowns.
@@ -342,6 +349,9 @@ public class LoanProductData implements Serializable {
final StringEnumOptionData chargeOffBehaviour = null; final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false; final boolean interestRecognitionOnDisbursementDate = false;
final StringEnumOptionData daysInYearTypeCustomStrategy = null; 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, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -363,7 +373,8 @@ public class LoanProductData implements Serializable {
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, 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 StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false; final boolean interestRecognitionOnDisbursementDate = false;
final StringEnumOptionData daysInYearTypeCustomStrategy = null; 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, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -489,7 +503,8 @@ public class LoanProductData implements Serializable {
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, 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 StringEnumOptionData chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR.getValueAsStringEnumOptionData();
final boolean interestRecognitionOnDisbursementDate = false; final boolean interestRecognitionOnDisbursementDate = false;
final StringEnumOptionData daysInYearTypeCustomStrategy = null; 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, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -622,7 +640,8 @@ public class LoanProductData implements Serializable {
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, 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 StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false; final boolean interestRecognitionOnDisbursementDate = false;
final StringEnumOptionData daysInYearTypeCustomStrategy = null; 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, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -749,7 +771,8 @@ public class LoanProductData implements Serializable {
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy); interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy, enableIncomeCapitalization,
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
} }
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings, 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 EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes,
StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate, StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
final StringEnumOptionData daysInYearCustomStrategy) { final StringEnumOptionData daysInYearCustomStrategy, final boolean enableIncomeCapitalization,
final StringEnumOptionData capitalizedIncomeCalculationType, final StringEnumOptionData capitalizedIncomeStrategy) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.shortName = shortName; this.shortName = shortName;
@@ -861,6 +885,9 @@ public class LoanProductData implements Serializable {
this.rates = rates; this.rates = rates;
this.isRatesEnabled = isRatesEnabled; this.isRatesEnabled = isRatesEnabled;
this.daysInYearCustomStrategy = daysInYearCustomStrategy; this.daysInYearCustomStrategy = daysInYearCustomStrategy;
this.enableIncomeCapitalization = enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
this.chargeOptions = null; this.chargeOptions = null;
this.penaltyOptions = null; this.penaltyOptions = null;
@@ -950,6 +977,8 @@ public class LoanProductData implements Serializable {
this.chargeOffReasonOptions = null; this.chargeOffReasonOptions = null;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate; this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
this.daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList(); this.daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList();
this.capitalizedIncomeCalculationTypeOptions = LoanCapitalizedIncomeCalculationType.getValuesAsStringEnumOptionDataList();
this.capitalizedIncomeStrategyOptions = LoanCapitalizedIncomeStrategy.getValuesAsStringEnumOptionDataList();
} }
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions, public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
@@ -974,7 +1003,9 @@ public class LoanProductData implements Serializable {
final List<EnumOptionData> creditAllocationAllocationTypes, final List<EnumOptionData> creditAllocationAllocationTypes,
final List<StringEnumOptionData> supportedInterestRefundTypesOptions, final List<StringEnumOptionData> supportedInterestRefundTypesOptions,
final List<StringEnumOptionData> chargeOffBehaviourOptions, final List<CodeValueData> chargeOffReasonOptions, 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.id = productData.id;
this.name = productData.name; this.name = productData.name;
@@ -1138,6 +1169,11 @@ public class LoanProductData implements Serializable {
this.interestRecognitionOnDisbursementDate = productData.interestRecognitionOnDisbursementDate; this.interestRecognitionOnDisbursementDate = productData.interestRecognitionOnDisbursementDate;
this.daysInYearCustomStrategyOptions = daysInYearCustomStrategyOptions; this.daysInYearCustomStrategyOptions = daysInYearCustomStrategyOptions;
this.daysInYearCustomStrategy = productData.daysInYearCustomStrategy; 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) { private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -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.data.FloatingRatePeriodData;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate; import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.fund.domain.Fund; 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.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; 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 LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate, 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.fund = fund;
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode; this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
@@ -330,7 +334,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(), inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy); supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy,
enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal, this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments, defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
@@ -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.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; 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.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; 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") @Column(name = "days_in_year_custom_strategy")
private DaysInYearCustomStrategyType daysInYearCustomStrategy; 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, public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal,
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod, final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
@@ -182,7 +195,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate, 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); final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData);
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType, return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
@@ -192,7 +207,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy); chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
} }
protected LoanProductRelatedDetail() { protected LoanProductRelatedDetail() {
@@ -213,7 +229,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final boolean enableAccrualActivityPosting, List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate, 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.currency = currency;
this.principal = defaultPrincipal; this.principal = defaultPrincipal;
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod; this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
@@ -251,6 +269,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.chargeOffBehaviour = chargeOffBehaviour; this.chargeOffBehaviour = chargeOffBehaviour;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate; this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
this.daysInYearCustomStrategy = daysInYearCustomStrategy; this.daysInYearCustomStrategy = daysInYearCustomStrategy;
this.enableIncomeCapitalization = enableIncomeCapitalization;
this.capitalizedIncomeCalculationType = capitalizedIncomeCalculationType;
this.capitalizedIncomeStrategy = capitalizedIncomeStrategy;
} }
private Integer defaultToNullIfZero(final Integer value) { private Integer defaultToNullIfZero(final Integer value) {
@@ -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.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData; import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData; 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.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
@@ -1135,7 +1137,9 @@ public class LoansApiResource {
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations, loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled, collectionData, overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled, collectionData,
LoanScheduleType.getValuesAsEnumOptionDataList(), LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), LoanScheduleType.getValuesAsEnumOptionDataList(), LoanScheduleProcessingType.getValuesAsEnumOptionDataList(),
loanTermVariations, DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList()); loanTermVariations, DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList(),
LoanCapitalizedIncomeCalculationType.getValuesAsStringEnumOptionDataList(),
LoanCapitalizedIncomeStrategy.getValuesAsStringEnumOptionDataList());
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters); mandatoryResponseParameters);
@@ -1206,6 +1206,12 @@ final class LoansApiResourceSwagger {
public StringEnumOptionData chargeOffBehaviour; public StringEnumOptionData chargeOffBehaviour;
@Schema(example = "false") @Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate; 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") @Schema(description = "GetLoansResponse")
@@ -1309,6 +1315,12 @@ final class LoansApiResourceSwagger {
public BigDecimal fixedEmiAmount; public BigDecimal fixedEmiAmount;
@Schema(example = "false") @Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate; 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; public List<PostLoansRequestChargeData> charges;
@@ -172,6 +172,8 @@ public class LoanAccountData {
private List<EnumOptionData> loanScheduleTypeOptions; private List<EnumOptionData> loanScheduleTypeOptions;
private List<EnumOptionData> loanScheduleProcessingTypeOptions; private List<EnumOptionData> loanScheduleProcessingTypeOptions;
private List<StringEnumOptionData> daysInYearCustomStrategyOptions; private List<StringEnumOptionData> daysInYearCustomStrategyOptions;
private List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions;
private List<StringEnumOptionData> capitalizedIncomeStrategyOptions;
@Transient @Transient
private BigDecimal feeChargesAtDisbursementCharged; private BigDecimal feeChargesAtDisbursementCharged;
@@ -274,6 +276,9 @@ public class LoanAccountData {
private EnumOptionData loanScheduleProcessingType; private EnumOptionData loanScheduleProcessingType;
private StringEnumOptionData chargeOffBehaviour; private StringEnumOptionData chargeOffBehaviour;
private Boolean enableIncomeCapitalization;
private StringEnumOptionData capitalizedIncomeCalculationType;
private StringEnumOptionData capitalizedIncomeStrategy;
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId, public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments, Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
@@ -464,7 +469,9 @@ public class LoanAccountData {
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment,
final boolean enableInstallmentLevelDelinquency, final EnumOptionData loanScheduleType, final boolean enableInstallmentLevelDelinquency, final EnumOptionData loanScheduleType,
final EnumOptionData loanScheduleProcessingType, final Integer fixedLength, final StringEnumOptionData chargeOffBehaviour, 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(); final CollectionData delinquent = CollectionData.template();
@@ -510,7 +517,9 @@ public class LoanAccountData {
.setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency).setLoanScheduleType(loanScheduleType) .setEnableInstallmentLevelDelinquency(enableInstallmentLevelDelinquency).setLoanScheduleType(loanScheduleType)
.setLoanScheduleProcessingType(loanScheduleProcessingType).setFixedLength(fixedLength) .setLoanScheduleProcessingType(loanScheduleProcessingType).setFixedLength(fixedLength)
.setChargeOffBehaviour(chargeOffBehaviour).setInterestRecognitionOnDisbursementDate(isInterestRecognitionOnDisbursementDate) .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 Collection<LoanAccountSummaryData> clientActiveLoanOptions, final List<RateData> rates, final Boolean isRatesEnabled,
final CollectionData delinquent, final List<EnumOptionData> loanScheduleTypeOptions, final CollectionData delinquent, final List<EnumOptionData> loanScheduleTypeOptions,
final List<EnumOptionData> loanScheduleProcessingTypeOptions, final List<LoanTermVariationsData> loanTermVariations, 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) // 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) .setClientActiveLoanOptions(clientActiveLoanOptions).setRates(rates).setIsRatesEnabled(isRatesEnabled)
.setDelinquent(delinquent).setLoanScheduleTypeOptions(loanScheduleTypeOptions) .setDelinquent(delinquent).setLoanScheduleTypeOptions(loanScheduleTypeOptions)
.setLoanScheduleProcessingTypeOptions(loanScheduleProcessingTypeOptions).setLoanTermVariations(loanTermVariations) .setLoanScheduleProcessingTypeOptions(loanScheduleProcessingTypeOptions).setLoanTermVariations(loanTermVariations)
.setDaysInYearCustomStrategyOptions(daysInYearCustomStrategyOptions); .setDaysInYearCustomStrategyOptions(daysInYearCustomStrategyOptions)
.setCapitalizedIncomeCalculationTypeOptions(capitalizedIncomeCalculationTypeOptions)
.setCapitalizedIncomeStrategyOptions(capitalizedIncomeStrategyOptions);
} }
public LoanAccountData associationsAndTemplate(final Collection<LoanProductData> productOptions, public LoanAccountData associationsAndTemplate(final Collection<LoanProductData> productOptions,
@@ -544,7 +544,10 @@ public class LoanScheduleAssembler {
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting(), loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting(),
loanProduct.getLoanProductRelatedDetail().getSupportedInterestRefundTypes(), loanProduct.getLoanProductRelatedDetail().getSupportedInterestRefundTypes(),
loanProduct.getLoanProductRelatedDetail().getChargeOffBehaviour(), interestRecognitionOnDisbursementDate, 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, private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
@@ -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.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate; import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
import org.apache.fineract.portfolio.fund.domain.Fund; 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.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator; 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.LoanScheduleProcessingType;
@@ -305,6 +307,13 @@ public class LoanProductAssembler {
chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR; 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, return new LoanProduct(fund, loanTransactionProcessingStrategy, loanProductPaymentAllocationRules, loanProductCreditAllocationRules,
name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate, interestMethod,
@@ -325,7 +334,7 @@ public class LoanProductAssembler {
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate,
daysInYearCustomStrategy); daysInYearCustomStrategy, enableIncomeCapitalization, capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
} }
@@ -26,6 +26,8 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType; import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; 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.AprCalculator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -296,6 +298,28 @@ public class LoanProductRelatedDetailUpdateUtil {
loanRepaymentScheduleDetail.updateInterestRecognitionOnDisbursementDate(newValue); 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; return actualChanges;
} }
@@ -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.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.Loan; 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.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; 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.is_floating_interest_rate as isFloatingInterestRate, "
+ " l.interest_rate_differential as interestRateDifferential, " + " l.interest_rate_differential as interestRateDifferential, "
+ " l.days_in_year_custom_strategy as daysInYearCustomStrategy, " + " 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, " + " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement, "
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, " + " 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, " + " 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 boolean interestRecognitionOnDisbursementDate = rs.getBoolean("interestRecognitionOnDisbursementDate");
final StringEnumOptionData daysInYearCustomStrategy = DaysInYearCustomStrategyType final StringEnumOptionData daysInYearCustomStrategy = DaysInYearCustomStrategyType
.getStringEnumOptionData(rs.getString("daysInYearCustomStrategy")); .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, return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
@@ -1117,7 +1127,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment, lastClosedBusinessDate, overpaidOnDate, isChargedOff, enableDownPayment, disbursedAmountPercentageForDownPayment,
enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), enableAutoRepaymentForDownPayment, enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(),
loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(), fixedLength, chargeOffBehaviour.getValueAsStringEnumOptionData(),
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy); interestRecognitionOnDisbursementDate, daysInYearCustomStrategy, enableIncomeCapitalization,
capitalizedIncomeCalculationType, capitalizedIncomeStrategy);
} }
} }
@@ -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.data.FundData;
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService; import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; 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.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
@@ -127,7 +129,10 @@ public class LoanProductsApiResource {
LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT, LoanApiConstants.fixedPrincipalPercentagePerInstallmentParamName, LoanProductConstants.DUE_DAYS_FOR_REPAYMENT_EVENT,
LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.OVER_DUE_DAYS_FOR_REPAYMENT_EVENT, LoanProductConstants.ENABLE_DOWN_PAYMENT,
LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT, LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT,
LoanProductConstants.REPAYMENT_START_DATE_TYPE, 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<>( private static final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions")); Arrays.asList("productId", "productName", "restrictedProducts", "allowedProducts", "productOptions"));
@@ -439,6 +444,10 @@ public class LoanProductsApiResource {
.retrieveCodeValuesByCode(LoanApiConstants.CHARGE_OFF_REASONS); .retrieveCodeValuesByCode(LoanApiConstants.CHARGE_OFF_REASONS);
final List<StringEnumOptionData> daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType final List<StringEnumOptionData> daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType
.getValuesAsStringEnumOptionDataList(); .getValuesAsStringEnumOptionDataList();
final List<StringEnumOptionData> capitalizedIncomeCalculationTypeOptions = LoanCapitalizedIncomeCalculationType
.getValuesAsStringEnumOptionDataList();
final List<StringEnumOptionData> capitalizedIncomeStrategyOptions = LoanCapitalizedIncomeStrategy
.getValuesAsStringEnumOptionDataList();
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions, return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
@@ -451,7 +460,7 @@ public class LoanProductsApiResource {
advancedPaymentAllocationTypes, LoanScheduleType.getValuesAsEnumOptionDataList(), advancedPaymentAllocationTypes, LoanScheduleType.getValuesAsEnumOptionDataList(),
LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes, LoanScheduleProcessingType.getValuesAsEnumOptionDataList(), creditAllocationTransactionTypes,
creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions, creditAllocationAllocationTypes, supportedInterestRefundTypesOptions, chargeOffBehaviourOptions, chargeOffReasonOptions,
daysInYearCustomStrategyOptions); daysInYearCustomStrategyOptions, capitalizedIncomeCalculationTypeOptions, capitalizedIncomeStrategyOptions);
} }
} }
@@ -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.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; 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.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor; 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.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES, LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES,
LoanProductConstants.CHARGE_OFF_BEHAVIOUR, LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, 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, private static final String[] SUPPORTED_LOAN_CONFIGURABLE_ATTRIBUTES = { LoanProductConstants.amortizationTypeParamName,
LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName, LoanProductConstants.interestTypeParamName, LoanProductConstants.transactionProcessingStrategyCodeParamName,
@@ -877,6 +882,8 @@ public final class LoanProductDataValidator {
"Charge off behaviour is only supported for Progressive loans"); "Charge off behaviour is only supported for Progressive loans");
} }
validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -1922,6 +1929,9 @@ public final class LoanProductDataValidator {
validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment, validateRepaymentPeriodWithGraceSettings(numberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment,
graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator); graceOnInterestCharged, recurringMoratoriumOnPrincipalPeriods, baseDataValidator);
validateIncomeCapitalization(transactionProcessingStrategyCode, element, baseDataValidator);
throwExceptionIfValidationWarningsExist(dataValidationErrors); 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) { private Integer defaultToZeroIfNull(Integer value) {
return value != null ? value : 0; return value != null ? value : 0;
} }
@@ -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.common.service.CommonEnumerations;
import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData; import org.apache.fineract.portfolio.delinquency.data.DelinquencyBucketData;
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService; 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.domain.LoanChargeOffBehaviour;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; 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, " + "lp.allow_variabe_installments as isVariableIntallmentsAllowed, " + "lvi.minimum_gap as minimumGap, "
+ "lvi.maximum_gap as maximumGap, dbuc.id as delinquencyBucketId, dbuc.name as delinquencyBucketName, " + "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.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 " + "lp.charge_off_behaviour as chargeOffBehaviour, " //
+ " left join m_fund f on f.id = lp.fund_id " + "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_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_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 " + " 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 String chargeOffBehaviourStr = rs.getString("chargeOffBehaviour");
final LoanChargeOffBehaviour loanChargeOffBehaviour = LoanChargeOffBehaviour.valueOf(chargeOffBehaviourStr); final LoanChargeOffBehaviour loanChargeOffBehaviour = LoanChargeOffBehaviour.valueOf(chargeOffBehaviourStr);
final boolean interestRecognitionOnDisbursementDate = rs.getBoolean("interestRecognitionOnDisbursementDate"); 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, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -576,7 +586,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(), enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
loanChargeOffBehaviour.getValueAsStringEnumOptionData(), interestRecognitionOnDisbursementDate, 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/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/0169_add_missing_permissions.xml" relativeToChangelogFile="true" />
<include file="parts/0170_days_in_year_custom_strategy.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> </databaseChangeLog>
@@ -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>
@@ -98,7 +98,8 @@ public class DefaultScheduledDateGeneratorTest {
Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST, BigDecimal.valueOf(36_000L), null, 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, 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, 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 // when
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate, 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, 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, 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, null, false, DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null, null,
false, null); false, null, false, null, null);
} }
private HolidayDetailDTO createHolidayDTO() { private HolidayDetailDTO createHolidayDTO() {
@@ -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)));
}
}