Skip to content

Commit

Permalink
FINERACT-2174: Allow for recognition of Interest on first day end of …
Browse files Browse the repository at this point in the history
…day - Introducing the flag
  • Loading branch information
Jose Alberto Hernandez authored and adamsaghy committed Feb 5, 2025
1 parent 7d0474c commit 3a5888e
Show file tree
Hide file tree
Showing 36 changed files with 366 additions and 414 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,14 @@
"null",
"boolean"
]
},
{
"default": null,
"name": "interestRecognitionOnDisbursementDate",
"type": [
"null",
"boolean"
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,14 @@
"null",
"org.apache.fineract.avro.loan.v1.DelinquencyBucketDataV1"
]
},
{
"default": null,
"name": "interestRecognitionOnDisbursementDate",
"type": [
"null",
"boolean"
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ public interface LoanApiConstants {
// Data Validator names
String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";

String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate";

// Loan Summary Transaction Types
List<Integer> LOAN_SUMMARY_TRANSACTION_TYPES = List.of(LoanTransactionType.CHARGE_ADJUSTMENT.getValue(), //
LoanTransactionType.CHARGEBACK.getValue(), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ public final class LoanApplicationTerms {
private boolean enableAccrualActivityPosting;
private List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes;
private LoanChargeOffBehaviour chargeOffBehaviour;
private boolean interestRecognitionOnDisbursementDate;

private LoanApplicationTerms(Builder builder) {
this.currency = builder.currency;
Expand Down Expand Up @@ -460,7 +461,7 @@ public static LoanApplicationTerms assembleFrom(final CurrencyData currency, fin
final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour) {
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {

final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
Expand All @@ -479,7 +480,8 @@ public static LoanApplicationTerms assembleFrom(final CurrencyData currency, fin
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour);
fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);

}

Expand Down Expand Up @@ -552,7 +554,8 @@ public static LoanApplicationTerms assembleFrom(final CurrencyData currency, fin
fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
loanScheduleType, loanScheduleProcessingType, fixedLength, loanProductRelatedDetail.isEnableAccrualActivityPosting(),
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour());
loanProductRelatedDetail.getSupportedInterestRefundTypes(), loanProductRelatedDetail.getChargeOffBehaviour(),
loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate());
}

private LoanApplicationTerms(final CurrencyData currency, final Integer loanTermFrequency,
Expand Down Expand Up @@ -582,7 +585,8 @@ private LoanApplicationTerms(final CurrencyData currency, final Integer loanTerm
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled,
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 List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes, final LoanChargeOffBehaviour chargeOffBehaviour,
final boolean interestRecognitionOnDisbursementDate) {

this.currency = currency;
this.loanTermFrequency = loanTermFrequency;
Expand Down Expand Up @@ -682,6 +686,7 @@ private LoanApplicationTerms(final CurrencyData currency, final Integer loanTerm
this.fixedLength = fixedLength;
this.supportedInterestRefundTypes = supportedInterestRefundTypes;
this.chargeOffBehaviour = chargeOffBehaviour;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
}

public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
Expand Down Expand Up @@ -1543,7 +1548,7 @@ public LoanProductRelatedDetail toLoanProductRelatedDetail() {
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting, this.supportedInterestRefundTypes,
this.chargeOffBehaviour);
this.chargeOffBehaviour, this.interestRecognitionOnDisbursementDate);
}

public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,5 @@ public interface LoanProductConstants {
String SUPPORTED_INTEREST_REFUND_TYPES = "supportedInterestRefundTypes";
String CHARGE_OFF_BEHAVIOUR = "chargeOffBehaviour";

String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate";
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ private PostLoanProductsRequest() {}
public Integer repaymentStartDateType;
@Schema(example = "false")
public Boolean enableAccrualActivityPosting;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;

// Interest Recalculation
@Schema(example = "false")
Expand Down Expand Up @@ -1421,6 +1423,8 @@ private GetLoanCharge() {}
public List<StringEnumOptionData> supportedInterestRefundTypes;
public List<GetLoanProductsTemplateResponse.GetLoanProductsChargeOffReasonOptions> chargeOffReasonOptions;
public StringEnumOptionData chargeOffBehaviour;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;
}

@Schema(description = "PutLoanProductsProductIdRequest")
Expand Down Expand Up @@ -1558,6 +1562,8 @@ private PutLoanProductsProductIdRequest() {}
public Boolean enableAutoRepaymentForDownPayment;
@Schema(example = "1")
public Integer repaymentStartDateType;
@Schema(example = "false")
public Boolean interestRecognitionOnDisbursementDate;

// Interest Recalculation
@Schema(example = "false")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ public class LoanProductData implements Serializable {

private final EnumOptionData loanScheduleType;
private final EnumOptionData loanScheduleProcessingType;
private final boolean interestRecognitionOnDisbursementDate;

/**
* Used when returning lookup information about loan product for dropdowns.
Expand Down Expand Up @@ -338,6 +339,7 @@ public static LoanProductData lookup(final Long id, final String name, final Boo
final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false;

return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
Expand All @@ -358,7 +360,8 @@ public static LoanProductData lookup(final Long id, final String name, final Boo
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour);
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);

}

Expand Down Expand Up @@ -461,6 +464,7 @@ public static LoanProductData lookupWithCurrency(final Long id, final String nam
final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false;

return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
Expand All @@ -481,7 +485,8 @@ public static LoanProductData lookupWithCurrency(final Long id, final String nam
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour);
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);

}

Expand Down Expand Up @@ -591,6 +596,7 @@ public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = LoanChargeOffBehaviour.REGULAR.getValueAsStringEnumOptionData();
final boolean interestRecognitionOnDisbursementDate = false;

return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
Expand All @@ -611,7 +617,8 @@ public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour);
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);

}

Expand Down Expand Up @@ -715,6 +722,7 @@ public static LoanProductData loanProductWithFloatingRates(final Long id, final
final boolean enableAccrualActivityPosting = false;
final List<StringEnumOptionData> supportedInterestRefundTypes = null;
final StringEnumOptionData chargeOffBehaviour = null;
final boolean interestRecognitionOnDisbursementDate = false;

return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
Expand All @@ -735,7 +743,8 @@ public static LoanProductData loanProductWithFloatingRates(final Long id, final
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour);
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour,
interestRecognitionOnDisbursementDate);
}

public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
Expand Down Expand Up @@ -789,7 +798,7 @@ public LoanProductData(final Long id, final String name, final String shortName,
final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency,
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<StringEnumOptionData> supportedInterestRefundTypes,
StringEnumOptionData chargeOffBehaviour) {
StringEnumOptionData chargeOffBehaviour, final boolean interestRecognitionOnDisbursementDate) {
this.id = id;
this.name = name;
this.shortName = shortName;
Expand Down Expand Up @@ -932,6 +941,7 @@ public LoanProductData(final Long id, final String name, final String shortName,
this.chargeOffBehaviour = chargeOffBehaviour;
this.chargeOffBehaviourOptions = null;
this.chargeOffReasonOptions = null;
this.interestRecognitionOnDisbursementDate = interestRecognitionOnDisbursementDate;
}

public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
Expand Down Expand Up @@ -1116,6 +1126,7 @@ public LoanProductData(final LoanProductData productData, final Collection<Charg
this.chargeOffBehaviour = productData.chargeOffBehaviour;
this.chargeOffBehaviourOptions = chargeOffBehaviourOptions;
this.chargeOffReasonOptions = chargeOffReasonOptions;
this.interestRecognitionOnDisbursementDate = productData.interestRecognitionOnDisbursementDate;
}

private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,12 @@ public static LoanProduct assembleFromJson(final Fund fund, final String loanTra
final boolean enableAccrualActivityPosting = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING);

boolean interestRecognitionOnDisbursementDate = false;
if (command.parameterExists(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE)) {
interestRecognitionOnDisbursementDate = command
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE);
}

List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes = new ArrayList<>();
if (command.parameterExists(LoanProductConstants.SUPPORTED_INTEREST_REFUND_TYPES)) {
JsonArray supportedTransactionsForInterestRefund = command
Expand Down Expand Up @@ -480,7 +486,7 @@ public static LoanProduct assembleFromJson(final Fund fund, final String loanTra
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour);
enableAccrualActivityPosting, supportedInterestRefundTypes, chargeOffBehaviour, interestRecognitionOnDisbursementDate);

}

Expand Down Expand Up @@ -699,7 +705,7 @@ public LoanProduct(final Fund fund, final String transactionProcessingStrategyCo
final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting, final List<LoanSupportedInterestRefundTypes> supportedInterestRefundTypes,
final LoanChargeOffBehaviour chargeOffBehaviour) {
final LoanChargeOffBehaviour chargeOffBehaviour, final boolean isInterestRecognitionOnDisbursementDate) {
this.fund = fund;
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;

Expand Down Expand Up @@ -749,7 +755,7 @@ public LoanProduct(final Fund fund, final String transactionProcessingStrategyCo
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting,
supportedInterestRefundTypes, chargeOffBehaviour);
supportedInterestRefundTypes, chargeOffBehaviour, isInterestRecognitionOnDisbursementDate);

this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
Expand Down Expand Up @@ -802,7 +808,6 @@ public LoanProduct(final Fund fund, final String transactionProcessingStrategyCo
this.repaymentStartDateType = repaymentStartDateType;

this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;

validateLoanProductPreSave();
}

Expand Down Expand Up @@ -870,6 +875,14 @@ public void validateLoanProductPreSave() {
}
}

if (this.getLoanProductRelatedDetail().isInterestRecognitionOnDisbursementDate()
&& this.getLoanProductRelatedDetail().getLoanScheduleType().equals(LoanScheduleType.CUMULATIVE)) {
throw new LoanProductGeneralRuleException(
"interestRecognitionOnDisbursementDate.is.only.supported.for.progressive.loan.schedule.type",
"interestRecognitionOnDisbursementDate is only supported for progressive loan schedule type");

}

}

public MonetaryCurrency getCurrency() {
Expand Down Expand Up @@ -1621,4 +1634,5 @@ public RepaymentStartDateType getRepaymentStartDateType() {
public void updateEnableInstallmentLevelDelinquency(boolean enableInstallmentLevelDelinquency) {
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
}

}
Loading

0 comments on commit 3a5888e

Please sign in to comment.