FINERACT-2197: Introduce a new type of ACTUAL during calculation of days in year
This commit is contained in:
+1
@@ -93,6 +93,7 @@ public interface LoanApiConstants {
|
||||
String linkAccountIdParameterName = "linkAccountId";
|
||||
String createStandingInstructionAtDisbursementParameterName = "createStandingInstructionAtDisbursement";
|
||||
String daysInYearTypeParameterName = "daysInYearType";
|
||||
String daysInYearCustomStrategyParameterName = "daysInYearCustomStrategy";
|
||||
String daysInMonthTypeParameterName = "daysInMonthType";
|
||||
|
||||
String MULTIDISBURSE_DETAILS_PARAMNAME = "multiDisburseDetails";
|
||||
|
||||
+19
-7
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
|
||||
import org.apache.fineract.portfolio.common.domain.DayOfWeekType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.NthDayType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
@@ -230,6 +231,7 @@ public final class LoanApplicationTerms {
|
||||
private List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes;
|
||||
private LoanChargeOffBehaviour chargeOffBehaviour;
|
||||
private boolean interestRecognitionOnDisbursementDate;
|
||||
private DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
|
||||
private LoanApplicationTerms(Builder builder) {
|
||||
this.currency = builder.currency;
|
||||
@@ -294,6 +296,7 @@ public final class LoanApplicationTerms {
|
||||
private LocalDate seedDate;
|
||||
private MathContext mc;
|
||||
private Boolean interestRecognitionOnDisbursementDate;
|
||||
private DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
|
||||
public Builder currency(CurrencyData currency) {
|
||||
this.currency = currency;
|
||||
@@ -413,6 +416,11 @@ public final class LoanApplicationTerms {
|
||||
public LoanApplicationTerms build() {
|
||||
return new LoanApplicationTerms(this);
|
||||
}
|
||||
|
||||
public Builder daysInYearCustomStrategy(DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(LoanRepaymentScheduleModelData modelData, MathContext mc) {
|
||||
@@ -439,7 +447,8 @@ public final class LoanApplicationTerms {
|
||||
.inArrearsTolerance(Money.zero(modelData.currency(), mc)).disbursementDatas(new ArrayList<>())
|
||||
.isDownPaymentEnabled(modelData.downPaymentEnabled()).downPaymentPercentage(downPaymentPercentage)
|
||||
.submittedOnDate(modelData.scheduleGenerationStartDate()).seedDate(seedDate)
|
||||
.interestRecognitionOnDisbursementDate(modelData.interestRecognitionOnDisbursementDate()).mc(mc).build();
|
||||
.interestRecognitionOnDisbursementDate(modelData.interestRecognitionOnDisbursementDate())
|
||||
.daysInYearCustomStrategy(modelData.daysInYearCustomStrategy()).mc(mc).build();
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final CurrencyData currency, final Integer loanTermFrequency,
|
||||
@@ -470,7 +479,8 @@ public final class LoanApplicationTerms {
|
||||
final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
@@ -490,7 +500,7 @@ public final class LoanApplicationTerms {
|
||||
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
|
||||
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate);
|
||||
interestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -564,7 +574,7 @@ public final class LoanApplicationTerms {
|
||||
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
|
||||
loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(),
|
||||
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour(),
|
||||
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate());
|
||||
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(), loanProductRelatedDetail.getDaysInYearCustomStrategy());
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency,
|
||||
@@ -595,7 +605,7 @@ public final class LoanApplicationTerms {
|
||||
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting,
|
||||
final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour,
|
||||
final boolean interestRecognitionOnDisbursementDate) {
|
||||
final boolean interestRecognitionOnDisbursementDate, final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
@@ -696,6 +706,7 @@ public final class LoanApplicationTerms {
|
||||
this.supportedInterestRefundTypes = supportedInterestRefundTypes;
|
||||
this.chargeOffBehaviour = chargeOffBehaviour;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1557,7 +1568,7 @@ public final class LoanApplicationTerms {
|
||||
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
|
||||
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
|
||||
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes,
|
||||
this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate);
|
||||
this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate, this.daysInYearCustomStrategy);
|
||||
}
|
||||
|
||||
public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
|
||||
@@ -1567,7 +1578,8 @@ public final class LoanApplicationTerms {
|
||||
interestPaymentGrace, principalGrace, recurringMoratoriumOnPrincipalPeriods, interestMethod,
|
||||
interestCalculationPeriodMethod, daysInYearType, daysInMonthType, amortizationMethod, repaymentPeriodFrequencyType,
|
||||
repaymentEvery, numberOfRepayments,
|
||||
isInterestChargedFromDateSameAsDisbursalDateEnabled != null && isInterestChargedFromDateSameAsDisbursalDateEnabled);
|
||||
isInterestChargedFromDateSameAsDisbursalDateEnabled != null && isInterestChargedFromDateSameAsDisbursalDateEnabled,
|
||||
daysInYearCustomStrategy);
|
||||
}
|
||||
|
||||
public Integer getLoanTermFrequency() {
|
||||
|
||||
+3
-11
@@ -18,12 +18,14 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
|
||||
public record LoanRepaymentScheduleModelData(@NotNull LocalDate scheduleGenerationStartDate, @NotNull CurrencyData currency,
|
||||
@@ -31,15 +33,5 @@ public record LoanRepaymentScheduleModelData(@NotNull LocalDate scheduleGenerati
|
||||
@NotNull int repaymentFrequency, @NotBlank String repaymentFrequencyType, @NotNull BigDecimal annualNominalInterestRate,
|
||||
@NotNull boolean downPaymentEnabled, @NotNull DaysInMonthType daysInMonth, @NotNull DaysInYearType daysInYear,
|
||||
BigDecimal downPaymentPercentage, Integer installmentAmountInMultiplesOf, Integer fixedLength,
|
||||
@NotNull Boolean interestRecognitionOnDisbursementDate) {
|
||||
|
||||
LoanRepaymentScheduleModelData(@NotNull LocalDate scheduleGenerationStartDate, @NotNull CurrencyData currency,
|
||||
@NotNull BigDecimal disbursementAmount, @NotNull LocalDate disbursementDate, @NotNull int numberOfRepayments,
|
||||
@NotNull int repaymentFrequency, @NotBlank String repaymentFrequencyType, @NotNull BigDecimal annualNominalInterestRate,
|
||||
@NotNull boolean downPaymentEnabled, @NotNull DaysInMonthType daysInMonth, @NotNull DaysInYearType daysInYear,
|
||||
BigDecimal downPaymentPercentage, Integer installmentAmountInMultiplesOf, Integer fixedLength) {
|
||||
this(scheduleGenerationStartDate, currency, disbursementAmount, disbursementDate, numberOfRepayments, repaymentFrequency,
|
||||
repaymentFrequencyType, annualNominalInterestRate, downPaymentEnabled, daysInMonth, daysInYear, downPaymentPercentage,
|
||||
installmentAmountInMultiplesOf, fixedLength, false);
|
||||
}
|
||||
@NotNull Boolean interestRecognitionOnDisbursementDate, @Nullable DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
}
|
||||
|
||||
+1
@@ -81,6 +81,7 @@ public interface LoanProductConstants {
|
||||
// Interest recalculation related
|
||||
String IS_INTEREST_RECALCULATION_ENABLED_PARAMETER_NAME = "isInterestRecalculationEnabled";
|
||||
String DAYS_IN_YEAR_TYPE_PARAMETER_NAME = "daysInYearType";
|
||||
String DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME = "daysInYearCustomStrategy";
|
||||
String DAYS_IN_MONTH_TYPE_PARAMETER_NAME = "daysInMonthType";
|
||||
String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod";
|
||||
String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod";
|
||||
|
||||
+29
@@ -134,6 +134,8 @@ final class LoanProductsApiResourceSwagger {
|
||||
public Integer daysInMonthType;
|
||||
@Schema(example = "1")
|
||||
public Integer daysInYearType;
|
||||
@Schema(example = "FULL_LEAP_YEAR")
|
||||
public String daysInYearCustomStrategy;
|
||||
@Schema(example = "true")
|
||||
public Boolean allowPartialPeriodInterestCalcualtion;
|
||||
@Schema(example = "179")
|
||||
@@ -457,6 +459,18 @@ final class LoanProductsApiResourceSwagger {
|
||||
public String description;
|
||||
}
|
||||
|
||||
static final class GetLoanProductsDaysInYearCustomStrategy {
|
||||
|
||||
private GetLoanProductsDaysInYearCustomStrategy() {}
|
||||
|
||||
@Schema(example = "FULL_LEAP_YEAR")
|
||||
public String id;
|
||||
@Schema(example = "DaysInYearCustomStrategyType.fullLeapYear")
|
||||
public String code;
|
||||
@Schema(example = "Full Leap Year")
|
||||
public String description;
|
||||
}
|
||||
|
||||
static final class GetLoanProductsRepaymentStartDateType {
|
||||
|
||||
private GetLoanProductsRepaymentStartDateType() {}
|
||||
@@ -614,6 +628,7 @@ final class LoanProductsApiResourceSwagger {
|
||||
public List<Integer> numberOfRepaymentVariationsForBorrowerCycle;
|
||||
public GetLoansProductsDaysInMonthType daysInMonthType;
|
||||
public GetLoansProductsDaysInYearType daysInYearType;
|
||||
public GetLoanProductsDaysInYearCustomStrategy daysInYearCustomStrategy;
|
||||
@Schema(example = "true")
|
||||
public Boolean isInterestRecalculationEnabled;
|
||||
public GetLoanProductsInterestRecalculationData interestRecalculationData;
|
||||
@@ -722,6 +737,18 @@ final class LoanProductsApiResourceSwagger {
|
||||
public String description;
|
||||
}
|
||||
|
||||
static final class GetLoanProductsDaysInYearCustomStrategyTemplateType {
|
||||
|
||||
private GetLoanProductsDaysInYearCustomStrategyTemplateType() {}
|
||||
|
||||
@Schema(example = "FULL_LEAP_YEAR")
|
||||
public String id;
|
||||
@Schema(example = "DaysInYearCustomStrategyType.fullLeapYear")
|
||||
public String code;
|
||||
@Schema(example = "Full Leap Year")
|
||||
public String description;
|
||||
}
|
||||
|
||||
static final class GetLoanProductsInterestRecalculationTemplateData {
|
||||
|
||||
private GetLoanProductsInterestRecalculationTemplateData() {}
|
||||
@@ -1073,6 +1100,7 @@ final class LoanProductsApiResourceSwagger {
|
||||
public GetLoanProductsTemplateResponse.GetLoanProductsAccountingRule accountingRule;
|
||||
public GetLoansProductsDaysInMonthTemplateType daysInMonthType;
|
||||
public GetLoanProductsDaysInYearTemplateType daysInYearType;
|
||||
public GetLoanProductsDaysInYearCustomStrategyTemplateType daysInYearCustomStrategy;
|
||||
@Schema(example = "false")
|
||||
public Boolean isInterestRecalculationEnabled;
|
||||
public GetLoanProductsInterestRecalculationTemplateData interestRecalculationData;
|
||||
@@ -1091,6 +1119,7 @@ final class LoanProductsApiResourceSwagger {
|
||||
public Set<GetLoanProductsValueConditionTypeOptions> valueConditionTypeOptions;
|
||||
public Set<GetLoansProductsDaysInMonthTemplateType> daysInMonthTypeOptions;
|
||||
public Set<GetLoanProductsInterestTemplateType> daysInYearTypeOptions;
|
||||
public Set<GetLoanProductsDaysInYearCustomStrategyTemplateType> daysInYearTypeCustomStrategyOptions;
|
||||
public Set<GetLoanProductsResponse.GetLoanProductsInterestRecalculationData.GetLoanProductsInterestRecalculationCompoundingType> interestRecalculationCompoundingTypeOptions;
|
||||
public Set<GetLoanProductsResponse.GetLoanProductsInterestRecalculationData.GetLoanProductsRescheduleStrategyType> rescheduleStrategyTypeOptions;
|
||||
public Set<GetLoanProductsResponse.GetLoanProductsInterestRecalculationData.GetLoanProductsInterestRecalculationCompoundingFrequencyType> interestRecalculationFrequencyTypeOptions;
|
||||
|
||||
+19
-8
@@ -40,6 +40,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.common.service.CommonEnumerations;
|
||||
@@ -235,6 +236,8 @@ public class LoanProductData implements Serializable {
|
||||
private final EnumOptionData loanScheduleType;
|
||||
private final EnumOptionData loanScheduleProcessingType;
|
||||
private final boolean interestRecognitionOnDisbursementDate;
|
||||
private final List<StringEnumOptionData> daysInYearCustomStrategyOptions;
|
||||
private final StringEnumOptionData daysInYearCustomStrategy;
|
||||
|
||||
/**
|
||||
* Used when returning lookup information about loan product for dropdowns.
|
||||
@@ -334,12 +337,11 @@ public class LoanProductData implements Serializable {
|
||||
final boolean enableInstallmentLevelDelinquency = false;
|
||||
final EnumOptionData loanScheduleType = null;
|
||||
final EnumOptionData loanScheduleProcessingType = null;
|
||||
final EnumOptionData loanScheduleTypeOptions = null;
|
||||
final EnumOptionData loanScheduleProcessingTypeOptions = null;
|
||||
final boolean enableAccrualActivityPosting = false;
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
|
||||
final StringEnumOptionData chargeOffBehaviour = null;
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -361,7 +363,7 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -465,6 +467,7 @@ public class LoanProductData implements Serializable {
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
|
||||
final StringEnumOptionData chargeOffBehaviour = null;
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -486,7 +489,7 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -597,6 +600,7 @@ public class LoanProductData implements Serializable {
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
|
||||
final StringEnumOptionData chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR.getValueAsStringEnumOptionData();
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -618,7 +622,7 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -723,6 +727,7 @@ public class LoanProductData implements Serializable {
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
|
||||
final StringEnumOptionData chargeOffBehaviour = null;
|
||||
final boolean interestRecognitionOnDisbursementDate = false;
|
||||
final StringEnumOptionData daysInYearTypeCustomStrategy = null;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -744,7 +749,7 @@ public class LoanProductData implements Serializable {
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
|
||||
interestRecognitionOnDisbursementDate);
|
||||
interestRecognitionOnDisbursementDate, daysInYearTypeCustomStrategy);
|
||||
}
|
||||
|
||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||
@@ -798,7 +803,8 @@ public class LoanProductData implements Serializable {
|
||||
final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency,
|
||||
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes,
|
||||
StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
|
||||
StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final StringEnumOptionData daysInYearCustomStrategy) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -854,6 +860,7 @@ public class LoanProductData implements Serializable {
|
||||
this.rateOptions = rateOptions;
|
||||
this.rates = rates;
|
||||
this.isRatesEnabled = isRatesEnabled;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
|
||||
this.chargeOptions = null;
|
||||
this.penaltyOptions = null;
|
||||
@@ -942,6 +949,7 @@ public class LoanProductData implements Serializable {
|
||||
this.chargeOffBehaviourOptions = null;
|
||||
this.chargeOffReasonOptions = null;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategyOptions = DaysInYearCustomStrategyType.getValuesAsStringEnumOptionDataList();
|
||||
}
|
||||
|
||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||
@@ -965,7 +973,8 @@ public class LoanProductData implements Serializable {
|
||||
final List<EnumOptionData> loanScheduleProcessingTypeOptions, final List<EnumOptionData> creditAllocationTransactionTypes,
|
||||
final List<EnumOptionData> creditAllocationAllocationTypes,
|
||||
final List<StringEnumOptionData> supportedInterestRefundTypesOptions,
|
||||
final List<StringEnumOptionData> chargeOffBehaviourOptions, final List<CodeValueData> chargeOffReasonOptions) {
|
||||
final List<StringEnumOptionData> chargeOffBehaviourOptions, final List<CodeValueData> chargeOffReasonOptions,
|
||||
final List<StringEnumOptionData> daysInYearCustomStrategyOptions) {
|
||||
|
||||
this.id = productData.id;
|
||||
this.name = productData.name;
|
||||
@@ -1127,6 +1136,8 @@ public class LoanProductData implements Serializable {
|
||||
this.chargeOffBehaviourOptions = chargeOffBehaviourOptions;
|
||||
this.chargeOffReasonOptions = chargeOffReasonOptions;
|
||||
this.interestRecognitionOnDisbursementDate = productData.interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategyOptions = daysInYearCustomStrategyOptions;
|
||||
this.daysInYearCustomStrategy = productData.daysInYearCustomStrategy;
|
||||
}
|
||||
|
||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||
|
||||
+10
-1
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanproduct.data;
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
@@ -46,13 +47,15 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe
|
||||
private final Integer repaymentEvery;
|
||||
private final Integer numberOfRepayments;
|
||||
private final boolean interestRecognitionOnDisbursementDate;
|
||||
private final DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
|
||||
public LoanProductRelatedDetailMinimumData(CurrencyData currency, BigDecimal interestRatePerPeriod,
|
||||
BigDecimal annualNominalInterestRate, Integer interestChargingGrace, Integer interestPaymentGrace, Integer principalGrace,
|
||||
Integer recurringMoratoriumOnPrincipalPeriods, InterestMethod interestMethod,
|
||||
InterestCalculationPeriodMethod interestCalculationPeriodMethod, DaysInYearType daysInYearType, DaysInMonthType daysInMonthType,
|
||||
AmortizationMethod amortizationMethod, PeriodFrequencyType repaymentPeriodFrequencyType, Integer repaymentEvery,
|
||||
Integer numberOfRepayments, boolean interestRecognitionOnDisbursementDate) {
|
||||
Integer numberOfRepayments, boolean interestRecognitionOnDisbursementDate,
|
||||
DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
this.currency = currency;
|
||||
this.interestRatePerPeriod = interestRatePerPeriod;
|
||||
this.annualNominalInterestRate = annualNominalInterestRate;
|
||||
@@ -69,6 +72,7 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe
|
||||
this.repaymentEvery = repaymentEvery;
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
}
|
||||
|
||||
private Integer defaultToNullIfZero(final Integer value) {
|
||||
@@ -168,4 +172,9 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe
|
||||
public boolean isInterestRecognitionOnDisbursementDate() {
|
||||
return interestRecognitionOnDisbursementDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaysInYearCustomStrategyType getDaysInYearCustomStrategy() {
|
||||
return daysInYearCustomStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
+21
-3
@@ -56,6 +56,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket;
|
||||
@@ -366,6 +367,9 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
final DaysInYearType daysInYearType = DaysInYearType
|
||||
.fromInt(command.integerValueOfParameterNamed(LoanProductConstants.DAYS_IN_YEAR_TYPE_PARAMETER_NAME));
|
||||
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy = command.enumValueOfParameterNamed(
|
||||
LoanProductConstants.DAYS_IN_YEAR_CUSTOM_STRATEGY_TYPE_PARAMETER_NAME, DaysInYearCustomStrategyType.class);
|
||||
|
||||
LoanProductInterestRecalculationDetails interestRecalculationSettings = null;
|
||||
|
||||
if (isInterestRecalculationEnabled) {
|
||||
@@ -486,7 +490,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
|
||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
|
||||
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate);
|
||||
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate,
|
||||
daysInYearCustomStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -705,7 +710,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType,
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate) {
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
||||
|
||||
@@ -755,7 +761,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
|
||||
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
|
||||
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate);
|
||||
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
|
||||
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
|
||||
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
|
||||
@@ -883,6 +889,18 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
|
||||
}
|
||||
|
||||
if (this.getLoanProductRelatedDetail().getDaysInYearCustomStrategy() != null
|
||||
&& this.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE)) {
|
||||
throw new LoanProductGeneralRuleException("days.in.year.custom.strategy.is.only.supported.for.progressive.loan.schedule.type",
|
||||
"daysInYearCustomStrategy is only supported for progressive loan schedule type");
|
||||
}
|
||||
|
||||
if (this.getLoanProductRelatedDetail().getDaysInYearCustomStrategy() != null
|
||||
&& !this.getLoanProductRelatedDetail().getDaysInYearType().equals(DaysInYearType.ACTUAL.getValue())) {
|
||||
throw new LoanProductGeneralRuleException("days.in.year.custom.strategy.is.only.applicable.for.actual.days.in.year.type",
|
||||
"daysInYearCustomStrategy is only applicable for ACTUAL days in year type");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
|
||||
+3
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
/**
|
||||
@@ -62,4 +63,6 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
Integer getDaysInYearType();
|
||||
|
||||
boolean isInterestRecognitionOnDisbursementDate();
|
||||
|
||||
DaysInYearCustomStrategyType getDaysInYearCustomStrategy();
|
||||
}
|
||||
|
||||
+16
-3
@@ -35,6 +35,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
@@ -168,6 +169,10 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
@Column(name = "interest_recognition_on_disbursement_date", nullable = false)
|
||||
private boolean interestRecognitionOnDisbursementDate = false;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "days_in_year_custom_strategy")
|
||||
private DaysInYearCustomStrategyType daysInYearCustomStrategy;
|
||||
|
||||
public static LoanProductRelatedDetail createFrom(final CurrencyData currencyData, final BigDecimal principal,
|
||||
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
|
||||
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
|
||||
@@ -181,7 +186,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
|
||||
final MonetaryCurrency currency = MonetaryCurrency.fromCurrencyData(currencyData);
|
||||
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
||||
@@ -191,7 +197,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
|
||||
loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes,
|
||||
chargeOffBehaviour, interestRecognitionOnDisbursementDate);
|
||||
chargeOffBehaviour, interestRecognitionOnDisbursementDate, daysInYearCustomStrategy);
|
||||
}
|
||||
|
||||
protected LoanProductRelatedDetail() {
|
||||
@@ -211,7 +217,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
|
||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||
final boolean enableAccrualActivityPosting, List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
|
||||
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate,
|
||||
final DaysInYearCustomStrategyType daysInYearCustomStrategy) {
|
||||
this.currency = currency;
|
||||
this.principal = defaultPrincipal;
|
||||
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
||||
@@ -248,6 +255,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.supportedInterestRefundTypes = supportedInterestRefundTypes;
|
||||
this.chargeOffBehaviour = chargeOffBehaviour;
|
||||
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
|
||||
this.daysInYearCustomStrategy = daysInYearCustomStrategy;
|
||||
}
|
||||
|
||||
private Integer defaultToNullIfZero(final Integer value) {
|
||||
@@ -295,6 +303,11 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
: BigDecimal.valueOf(Double.parseDouble(this.annualNominalInterestRate.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaysInYearCustomStrategyType getDaysInYearCustomStrategy() {
|
||||
return daysInYearCustomStrategy;
|
||||
}
|
||||
|
||||
// TODO: Move into assembler
|
||||
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.loanproduct.domain;
|
||||
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class LoanProductTest {
|
||||
|
||||
@Test
|
||||
void testCanCreateLoanProductWithDaysInYearCustomStrategy() {
|
||||
LoanProduct loanProduct = new LoanProduct();
|
||||
loanProduct.setLoanProductRelatedDetail(new LoanProductRelatedDetail());
|
||||
loanProduct.getLoanProductRelatedDetail().setLoanScheduleType(LoanScheduleType.PROGRESSIVE);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearType(1);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearCustomStrategy(DaysInYearCustomStrategyType.FEB_29_PERIOD_ONLY);
|
||||
loanProduct.validateLoanProductPreSave();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidationExceptionForCreateCumulativeLoanProductWithDaysInYearCustomStrategy() {
|
||||
LoanProduct loanProduct = new LoanProduct();
|
||||
loanProduct.setLoanProductRelatedDetail(new LoanProductRelatedDetail());
|
||||
loanProduct.getLoanProductRelatedDetail().setLoanScheduleType(LoanScheduleType.CUMULATIVE);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearType(1);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearCustomStrategy(DaysInYearCustomStrategyType.FEB_29_PERIOD_ONLY);
|
||||
Assertions.assertThrowsExactly(LoanProductGeneralRuleException.class, loanProduct::validateLoanProductPreSave);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidationExceptionForCreate360LoanProductWithDaysInYearCustomStrategy() {
|
||||
LoanProduct loanProduct = new LoanProduct();
|
||||
loanProduct.setLoanProductRelatedDetail(new LoanProductRelatedDetail());
|
||||
loanProduct.getLoanProductRelatedDetail().setLoanScheduleType(LoanScheduleType.PROGRESSIVE);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearType(360);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearCustomStrategy(DaysInYearCustomStrategyType.FEB_29_PERIOD_ONLY);
|
||||
Assertions.assertThrowsExactly(LoanProductGeneralRuleException.class, loanProduct::validateLoanProductPreSave);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCanCreate360LoanProductWithoutDaysInYearCustomStrategy() {
|
||||
LoanProduct loanProduct = new LoanProduct();
|
||||
loanProduct.setLoanProductRelatedDetail(new LoanProductRelatedDetail());
|
||||
loanProduct.getLoanProductRelatedDetail().setLoanScheduleType(LoanScheduleType.PROGRESSIVE);
|
||||
loanProduct.getLoanProductRelatedDetail().setDaysInYearType(360);
|
||||
loanProduct.validateLoanProductPreSave();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user