Skip to content

Commit

Permalink
FINERACT-2104: Accrual Activity Posting for Loans
Browse files Browse the repository at this point in the history
  • Loading branch information
somasorosdpc authored and adamsaghy committed Jul 9, 2024
1 parent 67440c4 commit 3f4bbf3
Show file tree
Hide file tree
Showing 27 changed files with 620 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* 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.infrastructure.event.business.domain.loan.transaction;

import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;

public class LoanTransactionAccrualActivityPostBusinessEvent extends LoanTransactionBusinessEvent {

private static final String TYPE = "LoanTransactionAccrualActivityPostBusinessEvent";

public LoanTransactionAccrualActivityPostBusinessEvent(LoanTransaction value) {
super(value);
}

@Override
public String getType() {
return TYPE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* 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.infrastructure.event.business.domain.loan.transaction;

import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBusinessEvent;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;

public class LoanTransactionAccrualActivityPreBusinessEvent extends LoanBusinessEvent {

private static final String TYPE = "LoanTransactionAccrualActivityPreBusinessEvent";

public LoanTransactionAccrualActivityPreBusinessEvent(Loan value) {
super(value);
}

@Override
public String getType() {
return TYPE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class LoanTransactionEnumData {
private final boolean downPayment;
private final boolean reAge;
private final boolean reAmortize;
private final boolean accrualActivity;

public LoanTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
Expand Down Expand Up @@ -90,6 +91,7 @@ public LoanTransactionEnumData(final Long id, final String code, final String va
this.chargeoff = Long.valueOf(27).equals(this.id);
this.downPayment = Long.valueOf(28).equals(this.id);
this.interestPaymentWaiver = Long.valueOf(31).equals(this.id);
this.accrualActivity = Long.valueOf(32).equals(this.id);
this.reAge = Long.valueOf(LoanTransactionType.REAGE.getValue()).equals(this.id);
this.reAmortize = Long.valueOf(LoanTransactionType.REAMORTIZE.getValue()).equals(this.id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ private Loan(final String accountNo, final Client client, final Group group, fin
this.loanInterestRecalculationDetails.updateLoan(this);
}
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
this.getLoanProductRelatedDetail()
.setEnableAccrualActivityPosting(loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
}

public Integer getNumberOfRepayments() {
Expand Down Expand Up @@ -4951,7 +4953,8 @@ rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInteres
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment,
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), repaymentStartDateType, getSubmittedOnDate());
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), repaymentStartDateType, getSubmittedOnDate(),
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
}

public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
Expand Down Expand Up @@ -5199,7 +5202,8 @@ public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency ap
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
false, this.fixedPrincipalPercentagePerInstallment, false, repaymentStartDateType, getSubmittedOnDate());
false, this.fixedPrincipalPercentagePerInstallment, false, repaymentStartDateType, getSubmittedOnDate(),
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,10 @@ public boolean isReAmortize() {
return getTypeOf().isReAmortize() && isNotReversed();
}

public boolean isAccrualActivity() {
return getTypeOf().isAccrualActivity();
}

public boolean isIdentifiedBy(final Long identifier) {
return getId().equals(identifier);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public enum LoanTransactionType {
REAGE(29, "loanTransactionType.reAge"), //
REAMORTIZE(30, "loanTransactionType.reAmortize"), //
INTEREST_PAYMENT_WAIVER(31, "loanTransactionType.interestPaymentWaiver"), //
ACCRUAL_ACTIVITY(32, "loanTransactionType.accrualActivity"), //
;

private final Integer value;
Expand Down Expand Up @@ -111,6 +112,7 @@ public static LoanTransactionType fromInt(final Integer transactionType) {
case 29 -> LoanTransactionType.REAGE;
case 30 -> LoanTransactionType.REAMORTIZE;
case 31 -> LoanTransactionType.INTEREST_PAYMENT_WAIVER;
case 32 -> LoanTransactionType.ACCRUAL_ACTIVITY;
default -> LoanTransactionType.INVALID;
};
}
Expand Down Expand Up @@ -215,4 +217,8 @@ public boolean isReAmortize() {
public boolean isDownPayment() {
return this.equals(LoanTransactionType.DOWN_PAYMENT);
}

public boolean isAccrualActivity() {
return this.equals(LoanTransactionType.ACCRUAL_ACTIVITY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ public final class LoanApplicationTerms {
private Money disbursedPrincipal;
private final LoanScheduleType loanScheduleType;
private final LoanScheduleProcessingType loanScheduleProcessingType;
private final boolean enableAccrualActivityPosting;

public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
Expand Down Expand Up @@ -251,7 +252,8 @@ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency curren
final Boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
final Boolean isAutoRepaymentForDownPaymentEnabled, final RepaymentStartDateType repaymentStartDateType,
final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength) {
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
final boolean enableAccrualActivityPosting) {

final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
Expand All @@ -270,7 +272,7 @@ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency curren
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
fixedLength);
fixedLength, enableAccrualActivityPosting);

}

Expand All @@ -291,7 +293,8 @@ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applic
final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI,
final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans,
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate) {
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate,
final boolean enableAccrualActivityPosting) {

final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
Expand Down Expand Up @@ -343,7 +346,7 @@ public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applic
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment,
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
fixedLength);
fixedLength, enableAccrualActivityPosting);
}

private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
Expand Down Expand Up @@ -372,7 +375,7 @@ private LoanApplicationTerms(final ApplicationCurrency currency, final Integer l
final boolean isPrincipalCompoundingDisabledForOverdueLoans, final boolean isDownPaymentEnabled,
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled,
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength) {
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength, boolean enableAccrualActivityPosting) {

this.currency = currency;
this.loanTermFrequency = loanTermFrequency;
Expand Down Expand Up @@ -426,6 +429,7 @@ private LoanApplicationTerms(final ApplicationCurrency currency, final Integer l
this.numberOfDays = numberOfDays;

this.loanCalendar = loanCalendar;
this.enableAccrualActivityPosting = enableAccrualActivityPosting;
this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount);
this.variationsDataWrapper = new LoanTermVariationsDataWrapper(loanTermVariations);
this.actualNumberOfRepayments = numberOfRepayments + getLoanTermVariations().adjustNumberOfRepayments();
Expand Down Expand Up @@ -1333,7 +1337,7 @@ public LoanProductRelatedDetail toLoanProductRelatedDetail() {
this.graceOnArrearsAgeing, this.daysInMonthType.getValue(), this.daysInYearType.getValue(),
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled, this.loanScheduleType,
this.loanScheduleProcessingType, this.fixedLength);
this.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting);
}

public Integer getLoanTermFrequency() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -52,6 +53,9 @@ CommandProcessingResult makeLoanRepayment(LoanTransactionType repaymentTransacti
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);

@Transactional
Loan makeAccrualActivityTransaction(Loan loan, LoanRepaymentScheduleInstallment installment, LocalDate currentDate);

@Transactional
CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,7 @@ public interface LoanProductConstants {
String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = "advanced-payment-allocation-strategy";

String FIXED_LENGTH = "fixedLength";

String ENABLE_ACCRUAL_ACTIVITY_POSTING = "enableAccrualActivityPosting";

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

// Interest Recalculation
@Schema(example = "false")
Expand Down Expand Up @@ -1277,6 +1279,8 @@ private GetLoanCharge() {}
public EnumOptionData loanScheduleType;
@Schema(example = "HORIZONTAL")
public EnumOptionData loanScheduleProcessingType;
@Schema(example = "false")
public Boolean enableAccrualActivityPosting;
}

@Schema(description = "PutLoanProductsProductIdRequest")
Expand Down Expand Up @@ -1487,6 +1491,8 @@ private PutLoanProductsProductIdRequest() {}
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
public List<GetLoanProductsProductIdResponse.GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings;
public List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
@Schema(example = "false")
public Boolean enableAccrualActivityPosting;

// Multi Disburse
@Schema(example = "true")
Expand Down
Loading

0 comments on commit 3f4bbf3

Please sign in to comment.