FINERACT-2104: Accrual Activity Posting for Loans
This commit is contained in:
+35
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -59,6 +59,7 @@ public class LoanTransactionEnumData {
|
|||||||
private final boolean downPayment;
|
private final boolean downPayment;
|
||||||
private final boolean reAge;
|
private final boolean reAge;
|
||||||
private final boolean reAmortize;
|
private final boolean reAmortize;
|
||||||
|
private final boolean accrualActivity;
|
||||||
|
|
||||||
public LoanTransactionEnumData(final Long id, final String code, final String value) {
|
public LoanTransactionEnumData(final Long id, final String code, final String value) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -90,6 +91,7 @@ public class LoanTransactionEnumData {
|
|||||||
this.chargeoff = Long.valueOf(27).equals(this.id);
|
this.chargeoff = Long.valueOf(27).equals(this.id);
|
||||||
this.downPayment = Long.valueOf(28).equals(this.id);
|
this.downPayment = Long.valueOf(28).equals(this.id);
|
||||||
this.interestPaymentWaiver = Long.valueOf(31).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.reAge = Long.valueOf(LoanTransactionType.REAGE.getValue()).equals(this.id);
|
||||||
this.reAmortize = Long.valueOf(LoanTransactionType.REAMORTIZE.getValue()).equals(this.id);
|
this.reAmortize = Long.valueOf(LoanTransactionType.REAMORTIZE.getValue()).equals(this.id);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -605,6 +605,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
this.loanInterestRecalculationDetails.updateLoan(this);
|
this.loanInterestRecalculationDetails.updateLoan(this);
|
||||||
}
|
}
|
||||||
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
|
this.enableInstallmentLevelDelinquency = enableInstallmentLevelDelinquency;
|
||||||
|
this.getLoanProductRelatedDetail()
|
||||||
|
.setEnableAccrualActivityPosting(loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getNumberOfRepayments() {
|
public Integer getNumberOfRepayments() {
|
||||||
@@ -4951,7 +4953,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
calendarHistoryDataWrapper, scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(),
|
||||||
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
holidayDetailDTO, allowCompoundingOnEod, scheduleGeneratorDTO.isFirstRepaymentDateAllowedOnHoliday(),
|
||||||
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment,
|
scheduleGeneratorDTO.isInterestToBeRecoveredFirstWhenGreaterThanEMI(), this.fixedPrincipalPercentagePerInstallment,
|
||||||
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), repaymentStartDateType, getSubmittedOnDate());
|
scheduleGeneratorDTO.isPrincipalCompoundingDisabledForOverdueLoans(), repaymentStartDateType, getSubmittedOnDate(),
|
||||||
|
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
|
public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
|
||||||
@@ -5199,7 +5202,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||||
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
|
||||||
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
|
calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod, false,
|
||||||
false, this.fixedPrincipalPercentagePerInstallment, false, repaymentStartDateType, getSubmittedOnDate());
|
false, this.fixedPrincipalPercentagePerInstallment, false, repaymentStartDateType, getSubmittedOnDate(),
|
||||||
|
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+4
@@ -711,6 +711,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return getTypeOf().isReAmortize() && isNotReversed();
|
return getTypeOf().isReAmortize() && isNotReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAccrualActivity() {
|
||||||
|
return getTypeOf().isAccrualActivity();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isIdentifiedBy(final Long identifier) {
|
public boolean isIdentifiedBy(final Long identifier) {
|
||||||
return getId().equals(identifier);
|
return getId().equals(identifier);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -64,6 +64,7 @@ public enum LoanTransactionType {
|
|||||||
REAGE(29, "loanTransactionType.reAge"), //
|
REAGE(29, "loanTransactionType.reAge"), //
|
||||||
REAMORTIZE(30, "loanTransactionType.reAmortize"), //
|
REAMORTIZE(30, "loanTransactionType.reAmortize"), //
|
||||||
INTEREST_PAYMENT_WAIVER(31, "loanTransactionType.interestPaymentWaiver"), //
|
INTEREST_PAYMENT_WAIVER(31, "loanTransactionType.interestPaymentWaiver"), //
|
||||||
|
ACCRUAL_ACTIVITY(32, "loanTransactionType.accrualActivity"), //
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
@@ -111,6 +112,7 @@ public enum LoanTransactionType {
|
|||||||
case 29 -> LoanTransactionType.REAGE;
|
case 29 -> LoanTransactionType.REAGE;
|
||||||
case 30 -> LoanTransactionType.REAMORTIZE;
|
case 30 -> LoanTransactionType.REAMORTIZE;
|
||||||
case 31 -> LoanTransactionType.INTEREST_PAYMENT_WAIVER;
|
case 31 -> LoanTransactionType.INTEREST_PAYMENT_WAIVER;
|
||||||
|
case 32 -> LoanTransactionType.ACCRUAL_ACTIVITY;
|
||||||
default -> LoanTransactionType.INVALID;
|
default -> LoanTransactionType.INVALID;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -215,4 +217,8 @@ public enum LoanTransactionType {
|
|||||||
public boolean isDownPayment() {
|
public boolean isDownPayment() {
|
||||||
return this.equals(LoanTransactionType.DOWN_PAYMENT);
|
return this.equals(LoanTransactionType.DOWN_PAYMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAccrualActivity() {
|
||||||
|
return this.equals(LoanTransactionType.ACCRUAL_ACTIVITY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-6
@@ -224,6 +224,7 @@ public final class LoanApplicationTerms {
|
|||||||
private Money disbursedPrincipal;
|
private Money disbursedPrincipal;
|
||||||
private final LoanScheduleType loanScheduleType;
|
private final LoanScheduleType loanScheduleType;
|
||||||
private final LoanScheduleProcessingType loanScheduleProcessingType;
|
private final LoanScheduleProcessingType loanScheduleProcessingType;
|
||||||
|
private final boolean enableAccrualActivityPosting;
|
||||||
|
|
||||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||||
@@ -251,7 +252,8 @@ public final class LoanApplicationTerms {
|
|||||||
final Boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
final Boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||||
final Boolean isAutoRepaymentForDownPaymentEnabled, final RepaymentStartDateType repaymentStartDateType,
|
final Boolean isAutoRepaymentForDownPaymentEnabled, final RepaymentStartDateType repaymentStartDateType,
|
||||||
final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
|
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 LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||||
@@ -270,7 +272,7 @@ public final class LoanApplicationTerms {
|
|||||||
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
||||||
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
|
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
|
||||||
fixedLength);
|
fixedLength, enableAccrualActivityPosting);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +293,8 @@ public final class LoanApplicationTerms {
|
|||||||
final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
final boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod,
|
||||||
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
final boolean isFirstRepaymentDateAllowedOnHoliday, final boolean isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans,
|
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 numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||||
@@ -343,7 +346,7 @@ public final class LoanApplicationTerms {
|
|||||||
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
||||||
isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment,
|
isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment,
|
||||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
|
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, loanScheduleType, loanScheduleProcessingType,
|
||||||
fixedLength);
|
fixedLength, enableAccrualActivityPosting);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
@@ -372,7 +375,7 @@ public final class LoanApplicationTerms {
|
|||||||
final boolean isPrincipalCompoundingDisabledForOverdueLoans, final boolean isDownPaymentEnabled,
|
final boolean isPrincipalCompoundingDisabledForOverdueLoans, final boolean isDownPaymentEnabled,
|
||||||
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled,
|
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled,
|
||||||
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate, final LoanScheduleType loanScheduleType,
|
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.currency = currency;
|
||||||
this.loanTermFrequency = loanTermFrequency;
|
this.loanTermFrequency = loanTermFrequency;
|
||||||
@@ -426,6 +429,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.numberOfDays = numberOfDays;
|
this.numberOfDays = numberOfDays;
|
||||||
|
|
||||||
this.loanCalendar = loanCalendar;
|
this.loanCalendar = loanCalendar;
|
||||||
|
this.enableAccrualActivityPosting = enableAccrualActivityPosting;
|
||||||
this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount);
|
this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount);
|
||||||
this.variationsDataWrapper = new LoanTermVariationsDataWrapper(loanTermVariations);
|
this.variationsDataWrapper = new LoanTermVariationsDataWrapper(loanTermVariations);
|
||||||
this.actualNumberOfRepayments = numberOfRepayments + getLoanTermVariations().adjustNumberOfRepayments();
|
this.actualNumberOfRepayments = numberOfRepayments + getLoanTermVariations().adjustNumberOfRepayments();
|
||||||
@@ -1333,7 +1337,7 @@ public final class LoanApplicationTerms {
|
|||||||
this.graceOnArrearsAgeing, this.daysInMonthType.getValue(), this.daysInYearType.getValue(),
|
this.graceOnArrearsAgeing, this.daysInMonthType.getValue(), this.daysInYearType.getValue(),
|
||||||
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.loanScheduleProcessingType, this.fixedLength, this.enableAccrualActivityPosting);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getLoanTermFrequency() {
|
public Integer getLoanTermFrequency() {
|
||||||
|
|||||||
+4
@@ -30,6 +30,7 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
|
|||||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
||||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
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.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -52,6 +53,9 @@ public interface LoanWritePlatformService {
|
|||||||
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
|
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
|
||||||
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
|
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
Loan makeAccrualActivityTransaction(Loan loan, LoanRepaymentScheduleInstallment installment, LocalDate currentDate);
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);
|
CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);
|
||||||
|
|
||||||
|
|||||||
+3
@@ -163,4 +163,7 @@ public interface LoanProductConstants {
|
|||||||
String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = "advanced-payment-allocation-strategy";
|
String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = "advanced-payment-allocation-strategy";
|
||||||
|
|
||||||
String FIXED_LENGTH = "fixedLength";
|
String FIXED_LENGTH = "fixedLength";
|
||||||
|
|
||||||
|
String ENABLE_ACCRUAL_ACTIVITY_POSTING = "enableAccrualActivityPosting";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -169,6 +169,8 @@ final class LoanProductsApiResourceSwagger {
|
|||||||
public Boolean enableAutoRepaymentForDownPayment;
|
public Boolean enableAutoRepaymentForDownPayment;
|
||||||
@Schema(example = "1")
|
@Schema(example = "1")
|
||||||
public Integer repaymentStartDateType;
|
public Integer repaymentStartDateType;
|
||||||
|
@Schema(example = "false")
|
||||||
|
public Boolean enableAccrualActivityPosting;
|
||||||
|
|
||||||
// Interest Recalculation
|
// Interest Recalculation
|
||||||
@Schema(example = "false")
|
@Schema(example = "false")
|
||||||
@@ -1277,6 +1279,8 @@ final class LoanProductsApiResourceSwagger {
|
|||||||
public EnumOptionData loanScheduleType;
|
public EnumOptionData loanScheduleType;
|
||||||
@Schema(example = "HORIZONTAL")
|
@Schema(example = "HORIZONTAL")
|
||||||
public EnumOptionData loanScheduleProcessingType;
|
public EnumOptionData loanScheduleProcessingType;
|
||||||
|
@Schema(example = "false")
|
||||||
|
public Boolean enableAccrualActivityPosting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "PutLoanProductsProductIdRequest")
|
@Schema(description = "PutLoanProductsProductIdRequest")
|
||||||
@@ -1487,6 +1491,8 @@ final class LoanProductsApiResourceSwagger {
|
|||||||
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
|
public List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings> paymentChannelToFundSourceMappings;
|
||||||
public List<GetLoanProductsProductIdResponse.GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings;
|
public List<GetLoanProductsProductIdResponse.GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings;
|
||||||
public List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
public List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
||||||
|
@Schema(example = "false")
|
||||||
|
public Boolean enableAccrualActivityPosting;
|
||||||
|
|
||||||
// Multi Disburse
|
// Multi Disburse
|
||||||
@Schema(example = "true")
|
@Schema(example = "true")
|
||||||
|
|||||||
+13
-5
@@ -148,6 +148,7 @@ public class LoanProductData implements Serializable {
|
|||||||
private Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings;
|
private Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings;
|
||||||
private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
||||||
private Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
private Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
|
||||||
|
private final boolean enableAccrualActivityPosting;
|
||||||
|
|
||||||
// rates
|
// rates
|
||||||
private final boolean isRatesEnabled;
|
private final boolean isRatesEnabled;
|
||||||
@@ -323,6 +324,7 @@ public class LoanProductData implements Serializable {
|
|||||||
final EnumOptionData loanScheduleProcessingType = null;
|
final EnumOptionData loanScheduleProcessingType = null;
|
||||||
final EnumOptionData loanScheduleTypeOptions = null;
|
final EnumOptionData loanScheduleTypeOptions = null;
|
||||||
final EnumOptionData loanScheduleProcessingTypeOptions = null;
|
final EnumOptionData loanScheduleProcessingTypeOptions = null;
|
||||||
|
final boolean enableAccrualActivityPosting = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -343,7 +345,7 @@ public class LoanProductData implements Serializable {
|
|||||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||||
loanScheduleProcessingType, fixedLength);
|
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,6 +445,7 @@ public class LoanProductData implements Serializable {
|
|||||||
final boolean enableInstallmentLevelDelinquency = false;
|
final boolean enableInstallmentLevelDelinquency = false;
|
||||||
final EnumOptionData loanScheduleType = null;
|
final EnumOptionData loanScheduleType = null;
|
||||||
final EnumOptionData loanScheduleProcessingType = null;
|
final EnumOptionData loanScheduleProcessingType = null;
|
||||||
|
final boolean enableAccrualActivityPosting = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -463,7 +466,7 @@ public class LoanProductData implements Serializable {
|
|||||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||||
loanScheduleProcessingType, fixedLength);
|
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,6 +573,7 @@ public class LoanProductData implements Serializable {
|
|||||||
final boolean enableInstallmentLevelDelinquency = false;
|
final boolean enableInstallmentLevelDelinquency = false;
|
||||||
final EnumOptionData loanScheduleType = LoanScheduleType.CUMULATIVE.asEnumOptionData();
|
final EnumOptionData loanScheduleType = LoanScheduleType.CUMULATIVE.asEnumOptionData();
|
||||||
final EnumOptionData loanScheduleProcessingType = LoanScheduleProcessingType.HORIZONTAL.asEnumOptionData();
|
final EnumOptionData loanScheduleProcessingType = LoanScheduleProcessingType.HORIZONTAL.asEnumOptionData();
|
||||||
|
final boolean enableAccrualActivityPosting = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -590,7 +594,7 @@ public class LoanProductData implements Serializable {
|
|||||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||||
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
paymentAllocation, creditAllocation, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||||
loanScheduleProcessingType, fixedLength);
|
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,6 +695,7 @@ public class LoanProductData implements Serializable {
|
|||||||
final boolean enableInstallmentLevelDelinquency = false;
|
final boolean enableInstallmentLevelDelinquency = false;
|
||||||
final EnumOptionData loanScheduleType = null;
|
final EnumOptionData loanScheduleType = null;
|
||||||
final EnumOptionData loanScheduleProcessingType = null;
|
final EnumOptionData loanScheduleProcessingType = null;
|
||||||
|
final boolean enableAccrualActivityPosting = false;
|
||||||
|
|
||||||
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,
|
||||||
@@ -711,7 +716,7 @@ public class LoanProductData implements Serializable {
|
|||||||
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
fixedPrincipalPercentagePerInstallment, delinquencyBucketOptions, delinquencyBucket, dueDaysForRepaymentEvent,
|
||||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||||
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
paymentAllocation, creditAllocationData, repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType,
|
||||||
loanScheduleProcessingType, fixedLength);
|
loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||||
@@ -761,7 +766,8 @@ public class LoanProductData implements Serializable {
|
|||||||
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment,
|
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean enableAutoRepaymentForDownPayment,
|
||||||
final Collection<AdvancedPaymentData> paymentAllocation, final Collection<CreditAllocationData> creditAllocation,
|
final Collection<AdvancedPaymentData> paymentAllocation, final Collection<CreditAllocationData> creditAllocation,
|
||||||
final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency,
|
final EnumOptionData repaymentStartDateType, final boolean enableInstallmentLevelDelinquency,
|
||||||
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength) {
|
final EnumOptionData loanScheduleType, final EnumOptionData loanScheduleProcessingType, final Integer fixedLength,
|
||||||
|
final boolean enableAccrualActivityPosting) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
@@ -897,6 +903,7 @@ public class LoanProductData implements Serializable {
|
|||||||
this.loanScheduleProcessingType = loanScheduleProcessingType;
|
this.loanScheduleProcessingType = loanScheduleProcessingType;
|
||||||
this.loanScheduleTypeOptions = null;
|
this.loanScheduleTypeOptions = null;
|
||||||
this.loanScheduleProcessingTypeOptions = null;
|
this.loanScheduleProcessingTypeOptions = null;
|
||||||
|
this.enableAccrualActivityPosting = enableAccrualActivityPosting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||||
@@ -1072,6 +1079,7 @@ public class LoanProductData implements Serializable {
|
|||||||
this.loanScheduleProcessingType = productData.getLoanScheduleProcessingType();
|
this.loanScheduleProcessingType = productData.getLoanScheduleProcessingType();
|
||||||
this.loanScheduleProcessingTypeOptions = loanScheduleProcessingTypeOptions;
|
this.loanScheduleProcessingTypeOptions = loanScheduleProcessingTypeOptions;
|
||||||
this.loanScheduleTypeOptions = loanScheduleTypeOptions;
|
this.loanScheduleTypeOptions = loanScheduleTypeOptions;
|
||||||
|
this.enableAccrualActivityPosting = productData.enableAccrualActivityPosting;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||||
|
|||||||
+15
-3
@@ -439,6 +439,9 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
|
|
||||||
final Integer fixedLength = command.integerValueOfParameterNamed(LoanProductConstants.FIXED_LENGTH);
|
final Integer fixedLength = command.integerValueOfParameterNamed(LoanProductConstants.FIXED_LENGTH);
|
||||||
|
|
||||||
|
final boolean enableAccrualActivityPosting = command
|
||||||
|
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING);
|
||||||
|
|
||||||
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,
|
||||||
@@ -457,7 +460,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment, disallowExpectedDisbursements,
|
isEqualAmortization, productRates, fixedPrincipalPercentagePerInstallment, disallowExpectedDisbursements,
|
||||||
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
|
allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber, dueDaysForRepaymentEvent,
|
||||||
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment, enableAutoRepaymentForDownPayment,
|
||||||
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength);
|
repaymentStartDateType, enableInstallmentLevelDelinquency, loanScheduleType, loanScheduleProcessingType, fixedLength,
|
||||||
|
enableAccrualActivityPosting);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +678,8 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||||
final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType,
|
final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType,
|
||||||
final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType,
|
final boolean enableInstallmentLevelDelinquency, final LoanScheduleType loanScheduleType,
|
||||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength) {
|
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||||
|
final boolean enableAccrualActivityPosting) {
|
||||||
this.fund = fund;
|
this.fund = fund;
|
||||||
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
||||||
|
|
||||||
@@ -723,7 +728,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
|
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
|
||||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
|
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
|
||||||
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||||
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength);
|
enableAutoRepaymentForDownPayment, loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting);
|
||||||
|
|
||||||
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
|
this.loanProductMinMaxConstraints = new LoanProductMinMaxConstraints(defaultMinPrincipal, defaultMaxPrincipal,
|
||||||
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
|
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
|
||||||
@@ -908,6 +913,13 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
|||||||
this.isLinkedToFloatingInterestRate = newValue;
|
this.isLinkedToFloatingInterestRate = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING,
|
||||||
|
this.getLoanProductRelatedDetail().isEnableAccrualActivityPosting())) {
|
||||||
|
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING);
|
||||||
|
actualChanges.put(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, newValue);
|
||||||
|
this.getLoanProductRelatedDetail().setEnableAccrualActivityPosting(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isLinkedToFloatingInterestRate) {
|
if (this.isLinkedToFloatingInterestRate) {
|
||||||
actualChanges.putAll(loanProductFloatingRates().update(command, floatingRate));
|
actualChanges.putAll(loanProductFloatingRates().update(command, floatingRate));
|
||||||
this.loanProductRelatedDetail.updateForFloatingInterestRates();
|
this.loanProductRelatedDetail.updateForFloatingInterestRates();
|
||||||
|
|||||||
+9
-3
@@ -150,6 +150,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private LoanScheduleProcessingType loanScheduleProcessingType;
|
private LoanScheduleProcessingType loanScheduleProcessingType;
|
||||||
|
|
||||||
|
@Column(name = "enable_accrual_activity_posting", nullable = false)
|
||||||
|
private boolean enableAccrualActivityPosting = false;
|
||||||
|
|
||||||
public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal,
|
public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, 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,
|
||||||
@@ -161,7 +164,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization,
|
final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization,
|
||||||
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||||
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
|
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
|
||||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength) {
|
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||||
|
final boolean enableAccrualActivityPosting) {
|
||||||
|
|
||||||
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
||||||
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion,
|
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion,
|
||||||
@@ -169,7 +173,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
|
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
|
||||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||||
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
|
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
|
||||||
loanScheduleType, loanScheduleProcessingType, fixedLength);
|
loanScheduleType, loanScheduleProcessingType, fixedLength, enableAccrualActivityPosting);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LoanProductRelatedDetail() {
|
protected LoanProductRelatedDetail() {
|
||||||
@@ -187,7 +191,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization,
|
final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization,
|
||||||
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||||
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
|
final boolean enableAutoRepaymentForDownPayment, final LoanScheduleType loanScheduleType,
|
||||||
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength) {
|
final LoanScheduleProcessingType loanScheduleProcessingType, final Integer fixedLength,
|
||||||
|
final boolean enableAccrualActivityPosting) {
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.principal = defaultPrincipal;
|
this.principal = defaultPrincipal;
|
||||||
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
||||||
@@ -220,6 +225,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
|||||||
this.enableAutoRepaymentForDownPayment = enableAutoRepaymentForDownPayment;
|
this.enableAutoRepaymentForDownPayment = enableAutoRepaymentForDownPayment;
|
||||||
this.loanScheduleType = loanScheduleType;
|
this.loanScheduleType = loanScheduleType;
|
||||||
this.loanScheduleProcessingType = loanScheduleProcessingType;
|
this.loanScheduleProcessingType = loanScheduleProcessingType;
|
||||||
|
this.enableAccrualActivityPosting = enableAccrualActivityPosting;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer defaultToNullIfZero(final Integer value) {
|
private Integer defaultToNullIfZero(final Integer value) {
|
||||||
|
|||||||
+2
@@ -319,6 +319,8 @@ public final class LoanEnumerations {
|
|||||||
"Re-age");
|
"Re-age");
|
||||||
case REAMORTIZE -> new LoanTransactionEnumData(LoanTransactionType.REAMORTIZE.getValue().longValue(),
|
case REAMORTIZE -> new LoanTransactionEnumData(LoanTransactionType.REAMORTIZE.getValue().longValue(),
|
||||||
LoanTransactionType.REAMORTIZE.getCode(), "Re-amortize");
|
LoanTransactionType.REAMORTIZE.getCode(), "Re-amortize");
|
||||||
|
case ACCRUAL_ACTIVITY -> new LoanTransactionEnumData(LoanTransactionType.ACCRUAL_ACTIVITY.getValue().longValue(),
|
||||||
|
LoanTransactionType.ACCRUAL_ACTIVITY.getCode(), "Accrual Activity");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* 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.cob.loan;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AccrualActivityPostingBusinessStep implements LoanCOBBusinessStep {
|
||||||
|
|
||||||
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
|
private final LoanAccountDomainService loanAccountDomainService;
|
||||||
|
private final ExternalIdFactory externalIdFactory;
|
||||||
|
private final LoanWritePlatformService loanWritePlatformService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loan execute(Loan loan) {
|
||||||
|
log.debug("start processing loan accrual activity posting on installment due date with id [{}]", loan.getId());
|
||||||
|
|
||||||
|
// check if loan capable for posting
|
||||||
|
if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
|
||||||
|
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||||
|
// check if loan has installment due on business day
|
||||||
|
Optional<LoanRepaymentScheduleInstallment> first = loan.getRepaymentScheduleInstallments().stream()
|
||||||
|
.filter(loanRepaymentScheduleInstallment -> loanRepaymentScheduleInstallment.getDueDate().isEqual(currentDate))
|
||||||
|
.findFirst();
|
||||||
|
if (first.isPresent()) {
|
||||||
|
final LoanRepaymentScheduleInstallment installment = first.get();
|
||||||
|
// check if there is no not-replayed-accrual-activity related to business date
|
||||||
|
List<LoanTransaction> loanTransactions = loan.getLoanTransactions(loanTransaction -> loanTransaction.isNotReversed()
|
||||||
|
&& loanTransaction.isAccrualActivity() && loanTransaction.getTransactionDate().isEqual(currentDate));
|
||||||
|
if (loanTransactions.isEmpty()) {
|
||||||
|
loan = loanWritePlatformService.makeAccrualActivityTransaction(loan, installment, currentDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("end processing loan accrual activity posting on installment due date with id [{}]", loan.getId());
|
||||||
|
return loan;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEnumStyledName() {
|
||||||
|
return "ACCRUAL_ACTIVITY_POSTING";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHumanReadableName() {
|
||||||
|
return "Accrual Activity Posting on Installment Due Date";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-1
@@ -511,7 +511,8 @@ public class LoanScheduleAssembler {
|
|||||||
allowCompoundingOnEod, isEqualAmortization, isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
allowCompoundingOnEod, isEqualAmortization, isInterestToBeRecoveredFirstWhenGreaterThanEMI,
|
||||||
fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
|
fixedPrincipalPercentagePerInstallment, isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled,
|
||||||
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
|
disbursedAmountPercentageForDownPayment, isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate,
|
||||||
loanScheduleType, loanScheduleProcessingType, fixedLength);
|
loanScheduleType, loanScheduleProcessingType, fixedLength,
|
||||||
|
loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting());
|
||||||
}
|
}
|
||||||
|
|
||||||
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery,
|
||||||
|
|||||||
+15
@@ -26,9 +26,11 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
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.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
@@ -65,4 +67,17 @@ public class LoanTransactionAssembler {
|
|||||||
return LoanTransaction.repaymentType(repaymentTransactionType, loan.getOffice(), repaymentAmount, paymentDetail, transactionDate,
|
return LoanTransaction.repaymentType(repaymentTransactionType, loan.getOffice(), repaymentAmount, paymentDetail, transactionDate,
|
||||||
txnExternalId, null);
|
txnExternalId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanTransaction assembleAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||||
|
final LocalDate transactionDate) {
|
||||||
|
MonetaryCurrency currency = loan.getCurrency();
|
||||||
|
ExternalId externalId = externalIdFactory.create();
|
||||||
|
|
||||||
|
BigDecimal interestPortion = installment.getInterestCharged(currency).getAmount();
|
||||||
|
BigDecimal feeChargesPortion = installment.getFeeChargesCharged(currency).getAmount();
|
||||||
|
BigDecimal penaltyChargesPortion = installment.getPenaltyChargesCharged(currency).getAmount();
|
||||||
|
BigDecimal transactionAmount = interestPortion.add(feeChargesPortion).add(penaltyChargesPortion);
|
||||||
|
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), transactionDate,
|
||||||
|
transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -84,6 +84,8 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.transaction
|
|||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPostBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPostBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPreBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPreBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanDisbursalTransactionBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanDisbursalTransactionBusinessEvent;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPostBusinessEvent;
|
||||||
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPreBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPostBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPostBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPreBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPreBusinessEvent;
|
||||||
@@ -1131,6 +1133,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public Loan makeAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
|
||||||
|
final LocalDate transactionDate) {
|
||||||
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
|
||||||
|
|
||||||
|
LoanTransaction newAccrualActivityTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
|
||||||
|
transactionDate);
|
||||||
|
|
||||||
|
loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
|
||||||
|
|
||||||
|
loan.addLoanTransaction(newAccrualActivityTransaction);
|
||||||
|
|
||||||
|
businessEventNotifierService
|
||||||
|
.notifyPostBusinessEvent(new LoanTransactionAccrualActivityPostBusinessEvent(newAccrualActivityTransaction));
|
||||||
|
|
||||||
|
return loan;
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult makeInterestPaymentWaiver(final JsonCommand command) {
|
public CommandProcessingResult makeInterestPaymentWaiver(final JsonCommand command) {
|
||||||
|
|||||||
+35
-1
@@ -106,6 +106,8 @@ public final class LoanProductDataValidator {
|
|||||||
public static final String MAX_DIFFERENTIAL_LENDING_RATE = "maxDifferentialLendingRate";
|
public static final String MAX_DIFFERENTIAL_LENDING_RATE = "maxDifferentialLendingRate";
|
||||||
public static final String IS_FLOATING_INTEREST_RATE_CALCULATION_ALLOWED = "isFloatingInterestRateCalculationAllowed";
|
public static final String IS_FLOATING_INTEREST_RATE_CALCULATION_ALLOWED = "isFloatingInterestRateCalculationAllowed";
|
||||||
public static final String ACCOUNTING_RULE = "accountingRule";
|
public static final String ACCOUNTING_RULE = "accountingRule";
|
||||||
|
public static final String ENABLE_ACCRUAL_ACTIVITY_POSTING = "enableAccrualActivityPosting";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parameters supported for this command.
|
* The parameters supported for this command.
|
||||||
*/
|
*/
|
||||||
@@ -172,7 +174,8 @@ public final class LoanProductDataValidator {
|
|||||||
LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
|
LoanProductConstants.ENABLE_DOWN_PAYMENT, LoanProductConstants.DISBURSED_AMOUNT_PERCENTAGE_DOWN_PAYMENT,
|
||||||
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.REPAYMENT_START_DATE_TYPE,
|
LoanProductConstants.ENABLE_AUTO_REPAYMENT_DOWN_PAYMENT, LoanProductConstants.REPAYMENT_START_DATE_TYPE,
|
||||||
LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.LOAN_SCHEDULE_TYPE,
|
LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, LoanProductConstants.LOAN_SCHEDULE_TYPE,
|
||||||
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH));
|
LoanProductConstants.LOAN_SCHEDULE_PROCESSING_TYPE, LoanProductConstants.FIXED_LENGTH,
|
||||||
|
LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING));
|
||||||
|
|
||||||
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,
|
||||||
@@ -734,6 +737,18 @@ public final class LoanProductDataValidator {
|
|||||||
.value(receivablePenaltyAccountId).notNull().integerGreaterThanZero();
|
.value(receivablePenaltyAccountId).notNull().integerGreaterThanZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!AccountingValidations.isAccrualBasedAccounting(accountingRuleType)
|
||||||
|
&& this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, element)) {
|
||||||
|
final Boolean enableAccrualActivityPosting = this.fromApiJsonHelper
|
||||||
|
.extractBooleanNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING).value(enableAccrualActivityPosting)
|
||||||
|
.ignoreIfNull().validateForBooleanValue();
|
||||||
|
if (!AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING).isOneOfTheseValues(Boolean.TRUE)
|
||||||
|
.failWithCode("should be false for non accrual accounting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element)) {
|
||||||
final Boolean useBorrowerCycle = this.fromApiJsonHelper
|
final Boolean useBorrowerCycle = this.fromApiJsonHelper
|
||||||
.extractBooleanNamed(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element);
|
.extractBooleanNamed(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME, element);
|
||||||
@@ -1613,6 +1628,25 @@ public final class LoanProductDataValidator {
|
|||||||
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed(ACCOUNTING_RULE, element, Locale.getDefault());
|
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed(ACCOUNTING_RULE, element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter(ACCOUNTING_RULE).value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 4);
|
baseDataValidator.reset().parameter(ACCOUNTING_RULE).value(accountingRuleType).ignoreIfNull().inMinMaxRange(1, 4);
|
||||||
|
|
||||||
|
boolean actualValue = loanProduct.getLoanProductRelatedDetail().isEnableAccrualActivityPosting();
|
||||||
|
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, element)) {
|
||||||
|
final Boolean enableAccrualActivityPosting = this.fromApiJsonHelper
|
||||||
|
.extractBooleanNamed(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING, element);
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING).value(enableAccrualActivityPosting)
|
||||||
|
.ignoreIfNull().validateForBooleanValue();
|
||||||
|
actualValue = enableAccrualActivityPosting;
|
||||||
|
}
|
||||||
|
if (actualValue) {
|
||||||
|
Integer ruleType = accountingRuleType;
|
||||||
|
if (ruleType == null) {
|
||||||
|
ruleType = loanProduct.getAccountingRule();
|
||||||
|
}
|
||||||
|
if (!AccountingValidations.isAccrualBasedAccounting(ruleType)) {
|
||||||
|
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING)
|
||||||
|
.failWithCode("should be false for non accrual accounting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), element);
|
final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), element);
|
||||||
baseDataValidator.reset().parameter(LoanProductAccountingParams.FUND_SOURCE.getValue()).value(fundAccountId).ignoreIfNull()
|
baseDataValidator.reset().parameter(LoanProductAccountingParams.FUND_SOURCE.getValue()).value(fundAccountId).ignoreIfNull()
|
||||||
.integerGreaterThanZero();
|
.integerGreaterThanZero();
|
||||||
|
|||||||
+3
-2
@@ -234,7 +234,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
|
+ "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, "
|
||||||
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,lp.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion, "
|
+ "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,lp.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion, "
|
||||||
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
|
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
|
||||||
+ "lp.fixed_length as fixedLength, "
|
+ "lp.fixed_length as fixedLength, " + "lp.enable_accrual_activity_posting as enableAccrualActivityPosting, "
|
||||||
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.recurring_moratorium_principal_periods as recurringMoratoriumOnPrincipalPeriods, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing,lp.overdue_days_for_npa as overdueDaysForNPA, "
|
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.recurring_moratorium_principal_periods as recurringMoratoriumOnPrincipalPeriods, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing,lp.overdue_days_for_npa as overdueDaysForNPA, "
|
||||||
+ "lp.min_days_between_disbursal_and_first_repayment As minimumDaysBetweenDisbursalAndFirstRepayment, "
|
+ "lp.min_days_between_disbursal_and_first_repayment As minimumDaysBetweenDisbursalAndFirstRepayment, "
|
||||||
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
|
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
|
||||||
@@ -525,6 +525,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
final LoanScheduleType loanScheduleType = LoanScheduleType.valueOf(loanScheduleTypeStr);
|
final LoanScheduleType loanScheduleType = LoanScheduleType.valueOf(loanScheduleTypeStr);
|
||||||
final String loanScheduleProcessingTypeStr = rs.getString("loanScheduleProcessingType");
|
final String loanScheduleProcessingTypeStr = rs.getString("loanScheduleProcessingType");
|
||||||
final LoanScheduleProcessingType loanScheduleProcessingType = LoanScheduleProcessingType.valueOf(loanScheduleProcessingTypeStr);
|
final LoanScheduleProcessingType loanScheduleProcessingType = LoanScheduleProcessingType.valueOf(loanScheduleProcessingTypeStr);
|
||||||
|
final boolean enableAccrualActivityPosting = rs.getBoolean("enableAccrualActivityPosting");
|
||||||
|
|
||||||
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,
|
||||||
@@ -547,7 +548,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
|||||||
dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||||
enableAutoRepaymentForDownPayment, advancedPaymentData, creditAllocationData, repaymentStartDateType,
|
enableAutoRepaymentForDownPayment, advancedPaymentData, creditAllocationData, repaymentStartDateType,
|
||||||
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
|
enableInstallmentLevelDelinquency, loanScheduleType.asEnumOptionData(), loanScheduleProcessingType.asEnumOptionData(),
|
||||||
fixedLength);
|
fixedLength, enableAccrualActivityPosting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,4 +161,5 @@
|
|||||||
<include file="parts/0139_add_disburse_without_auto_payment_command.xml" relativeToChangelogFile="true" />
|
<include file="parts/0139_add_disburse_without_auto_payment_command.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0140_trial_balance_with_asset_transfer_update.xml" relativeToChangelogFile="true" />
|
<include file="parts/0140_trial_balance_with_asset_transfer_update.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0141_add_interest_payment_waiver_transaction_type.xml" relativeToChangelogFile="true" />
|
<include file="parts/0141_add_interest_payment_waiver_transaction_type.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0142_add_accrual_activity_transaction.xml" relativeToChangelogFile="true" />
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
<?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.1.xsd">
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<addColumn tableName="m_product_loan">
|
||||||
|
<column defaultValueBoolean="false" type="boolean" name="enable_accrual_activity_posting">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet author="fineract" id="2">
|
||||||
|
<addColumn tableName="m_loan">
|
||||||
|
<column defaultValueBoolean="false" type="boolean" name="enable_accrual_activity_posting">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet id="3" author="fineract">
|
||||||
|
<insert tableName="m_external_event_configuration">
|
||||||
|
<column name="type" value="LoanTransactionAccrualActivityPostBusinessEvent"/>
|
||||||
|
<column name="enabled" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="m_external_event_configuration">
|
||||||
|
<column name="type" value="LoanTransactionAccrualActivityPreBusinessEvent"/>
|
||||||
|
<column name="enabled" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
<insert tableName="r_enum_value">
|
||||||
|
<column name="enum_name" value="transaction_type_enum"/>
|
||||||
|
<column name="enum_id" valueNumeric="32"/>
|
||||||
|
<column name="enum_message_property" value="Accrual Activity"/>
|
||||||
|
<column name="enum_value" value="Accrual Activity"/>
|
||||||
|
<column name="enum_type" valueBoolean="false"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
<changeSet id="4" author="fineract">
|
||||||
|
<insert tableName="m_batch_business_steps">
|
||||||
|
<column name="job_name" value="LOAN_CLOSE_OF_BUSINESS"/>
|
||||||
|
<column name="step_name" value="ACCRUAL_ACTIVITY_POSTING"/>
|
||||||
|
<column name="step_order" value="8"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+4
-2
@@ -102,7 +102,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
|||||||
"LoanReAgeTransactionBusinessEvent", "LoanUndoReAgeTransactionBusinessEvent", "LoanReAmortizeTransactionBusinessEvent",
|
"LoanReAgeTransactionBusinessEvent", "LoanUndoReAgeTransactionBusinessEvent", "LoanReAmortizeTransactionBusinessEvent",
|
||||||
"LoanUndoReAmortizeTransactionBusinessEvent", "LoanReAgeBusinessEvent", "LoanUndoReAgeBusinessEvent",
|
"LoanUndoReAmortizeTransactionBusinessEvent", "LoanReAgeBusinessEvent", "LoanUndoReAgeBusinessEvent",
|
||||||
"LoanReAmortizeBusinessEvent", "LoanUndoReAmortizeBusinessEvent", "LoanTransactionInterestPaymentWaiverPreBusinessEvent",
|
"LoanReAmortizeBusinessEvent", "LoanUndoReAmortizeBusinessEvent", "LoanTransactionInterestPaymentWaiverPreBusinessEvent",
|
||||||
"LoanTransactionInterestPaymentWaiverPostBusinessEvent");
|
"LoanTransactionInterestPaymentWaiverPostBusinessEvent", "LoanTransactionAccrualActivityPostBusinessEvent",
|
||||||
|
"LoanTransactionAccrualActivityPreBusinessEvent");
|
||||||
|
|
||||||
List<FineractPlatformTenant> tenants = Arrays
|
List<FineractPlatformTenant> tenants = Arrays
|
||||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||||
@@ -186,7 +187,8 @@ public class ExternalEventConfigurationValidationServiceTest {
|
|||||||
"LoanReAgeTransactionBusinessEvent", "LoanUndoReAgeTransactionBusinessEvent", "LoanReAmortizeTransactionBusinessEvent",
|
"LoanReAgeTransactionBusinessEvent", "LoanUndoReAgeTransactionBusinessEvent", "LoanReAmortizeTransactionBusinessEvent",
|
||||||
"LoanUndoReAmortizeTransactionBusinessEvent", "LoanReAgeBusinessEvent", "LoanUndoReAgeBusinessEvent",
|
"LoanUndoReAmortizeTransactionBusinessEvent", "LoanReAgeBusinessEvent", "LoanUndoReAgeBusinessEvent",
|
||||||
"LoanReAmortizeBusinessEvent", "LoanUndoReAmortizeBusinessEvent", "LoanTransactionInterestPaymentWaiverPreBusinessEvent",
|
"LoanReAmortizeBusinessEvent", "LoanUndoReAmortizeBusinessEvent", "LoanTransactionInterestPaymentWaiverPreBusinessEvent",
|
||||||
"LoanTransactionInterestPaymentWaiverPostBusinessEvent");
|
"LoanTransactionInterestPaymentWaiverPostBusinessEvent", "LoanTransactionAccrualActivityPostBusinessEvent",
|
||||||
|
"LoanTransactionAccrualActivityPreBusinessEvent");
|
||||||
|
|
||||||
List<FineractPlatformTenant> tenants = Arrays
|
List<FineractPlatformTenant> tenants = Arrays
|
||||||
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
|
||||||
|
|||||||
+2
-2
@@ -95,7 +95,7 @@ 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);
|
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
List<PreGeneratedLoanSchedulePeriod> result = underTest.generateRepaymentPeriods(expectedDisbursementDate, loanApplicationTerms,
|
List<PreGeneratedLoanSchedulePeriod> result = underTest.generateRepaymentPeriods(expectedDisbursementDate, loanApplicationTerms,
|
||||||
@@ -166,7 +166,7 @@ public class DefaultScheduledDateGeneratorTest {
|
|||||||
null, null, null, null, null, Money.of(fromApplicationCurrency(dollarCurrency), ZERO), false, null, EMPTY_LIST,
|
null, null, null, null, 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,
|
BigDecimal.valueOf(36_000L), null, DaysInMonthType.ACTUAL, DaysInYearType.ACTUAL, false, null, null, null, null, null, ZERO,
|
||||||
null, NONE, null, ZERO, EMPTY_LIST, true, 0, false, holidayDetailDTO, false, false, false, null, false, false, null, false,
|
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);
|
DISBURSEMENT_DATE, submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HolidayDetailDTO createHolidayDTO() {
|
private HolidayDetailDTO createHolidayDTO() {
|
||||||
|
|||||||
+3
-3
@@ -136,9 +136,9 @@ public abstract class BaseLoanIntegrationTest {
|
|||||||
// expense
|
// expense
|
||||||
protected final Account chargeOffExpenseAccount = accountHelper.createExpenseAccount("chargeOff");
|
protected final Account chargeOffExpenseAccount = accountHelper.createExpenseAccount("chargeOff");
|
||||||
protected final Account chargeOffFraudExpenseAccount = accountHelper.createExpenseAccount("chargeOffFraud");
|
protected final Account chargeOffFraudExpenseAccount = accountHelper.createExpenseAccount("chargeOffFraud");
|
||||||
protected final Account writtenOffAccount = accountHelper.createExpenseAccount();
|
protected final Account writtenOffAccount = accountHelper.createExpenseAccount("writtenOffAccount");
|
||||||
protected final Account goodwillExpenseAccount = accountHelper.createExpenseAccount();
|
protected final Account goodwillExpenseAccount = accountHelper.createExpenseAccount("goodwillExpenseAccount");
|
||||||
protected final Account goodwillIncomeAccount = accountHelper.createIncomeAccount();
|
protected final Account goodwillIncomeAccount = accountHelper.createIncomeAccount("goodwillIncomeAccount");
|
||||||
protected final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
protected final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||||
protected JournalEntryHelper journalEntryHelper = new JournalEntryHelper(requestSpec, responseSpec);
|
protected JournalEntryHelper journalEntryHelper = new JournalEntryHelper(requestSpec, responseSpec);
|
||||||
protected ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
|
protected ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
|
||||||
|
|||||||
+228
@@ -0,0 +1,228 @@
|
|||||||
|
/**
|
||||||
|
* 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 static org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder.DEFAULT_STRATEGY;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
import io.restassured.builder.RequestSpecBuilder;
|
||||||
|
import io.restassured.builder.ResponseSpecBuilder;
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
|
import io.restassured.specification.RequestSpecification;
|
||||||
|
import io.restassured.specification.ResponseSpecification;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import org.apache.fineract.client.models.PostChargesRequest;
|
||||||
|
import org.apache.fineract.client.models.PostChargesResponse;
|
||||||
|
import org.apache.fineract.client.models.PostClientsResponse;
|
||||||
|
import org.apache.fineract.client.models.PostLoanProductsRequest;
|
||||||
|
import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
|
||||||
|
import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
|
||||||
|
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
||||||
|
import org.apache.fineract.client.models.PostLoansRequest;
|
||||||
|
import org.apache.fineract.integrationtests.common.BusinessStepHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
|
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||||
|
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||||
|
import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
|
||||||
|
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||||
|
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||||
|
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ExtendWith(LoanTestLifecycleExtension.class)
|
||||||
|
public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrationTest {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(LoanTransactionAccrualActivityPostingTest.class);
|
||||||
|
private static final String DATETIME_PATTERN = "dd MMMM yyyy";
|
||||||
|
private static ResponseSpecification responseSpec;
|
||||||
|
private static RequestSpecification requestSpec;
|
||||||
|
private static LoanTransactionHelper loanTransactionHelper;
|
||||||
|
private static PostClientsResponse client;
|
||||||
|
private static ChargesHelper chargesHelper;
|
||||||
|
private static InlineLoanCOBHelper inlineLoanCOBHelper;
|
||||||
|
private static BusinessStepHelper businessStepHelper;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setup() {
|
||||||
|
Utils.initializeRESTAssured();
|
||||||
|
requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||||
|
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||||
|
requestSpec.header("Fineract-Platform-TenantId", "default");
|
||||||
|
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||||
|
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||||
|
ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
|
||||||
|
chargesHelper = new ChargesHelper();
|
||||||
|
client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
|
||||||
|
inlineLoanCOBHelper = new InlineLoanCOBHelper(requestSpec, responseSpec);
|
||||||
|
businessStepHelper = new BusinessStepHelper();
|
||||||
|
// setup COB Business Steps to prevent test failing due other integration test configurations
|
||||||
|
businessStepHelper.updateSteps("LOAN_CLOSE_OF_BUSINESS", "APPLY_CHARGE_TO_OVERDUE_LOANS", "LOAN_DELINQUENCY_CLASSIFICATION",
|
||||||
|
"CHECK_LOAN_REPAYMENT_DUE", "CHECK_LOAN_REPAYMENT_OVERDUE", "UPDATE_LOAN_ARREARS_AGING", "ADD_PERIODIC_ACCRUAL_ENTRIES",
|
||||||
|
"EXTERNAL_ASSET_OWNER_TRANSFER", "ACCRUAL_ACTIVITY_POSTING");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Loan with Interest and enabled Accrual Activity Posting
|
||||||
|
// Approve and disburse loan
|
||||||
|
// charge penalty with due date as 1st installment
|
||||||
|
// charge fee with due date as 1st installment
|
||||||
|
// charge penalty with due date as 3rd installment
|
||||||
|
// charge fee with due date as 2nd installment
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
final String disbursementDay = "01 January 2023";
|
||||||
|
final String repaymentPeriod1DueDate = "01 February 2023";
|
||||||
|
final String repaymentPeriod1CloseDate = "02 February 2023";
|
||||||
|
final String repaymentPeriod1OneDayBeforeCloseDate = "01 February 2023";
|
||||||
|
final String repaymentPeriod1OneDayAfterCloseDate = "03 February 2023";
|
||||||
|
final String repaymentPeriod2DueDate = "01 March 2023";
|
||||||
|
final String repaymentPeriod3DueDate = "01 April 2023";
|
||||||
|
final String creationBusinessDay = "15 May 2023";
|
||||||
|
AtomicReference<Long> loanId = new AtomicReference<>();
|
||||||
|
runAt(creationBusinessDay, () -> {
|
||||||
|
|
||||||
|
Long localLoanProductId = createLoanProductAccountingAccrualPeriodicWithInterest();
|
||||||
|
loanId.set(applyForLoanApplicationWithInterest(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40000),
|
||||||
|
disbursementDay));
|
||||||
|
|
||||||
|
LOG.info("Test Loan Product Id {1} http://localhost:4200/#/products/loan-products/{}/general", localLoanProductId);
|
||||||
|
LOG.info("Test Client Id {1} http://localhost:4200/#/clients/{}", client.getClientId());
|
||||||
|
LOG.info("Test Loan Id {2} http://localhost:4200/#/clients/{}/loans-accounts/{}/transactions", client.getClientId(), loanId);
|
||||||
|
|
||||||
|
loanTransactionHelper.approveLoan(loanId.get(), new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000))
|
||||||
|
.dateFormat(DATETIME_PATTERN).approvedOnDate(disbursementDay).locale("en"));
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay)
|
||||||
|
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
|
||||||
|
|
||||||
|
chargePenalty(loanId.get(), 30.0, repaymentPeriod1DueDate);
|
||||||
|
chargePenalty(loanId.get(), 50.0, repaymentPeriod2DueDate);
|
||||||
|
chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
|
||||||
|
chargeFee(loanId.get(), 60.0, repaymentPeriod3DueDate);
|
||||||
|
|
||||||
|
});
|
||||||
|
runAt(repaymentPeriod1OneDayBeforeCloseDate, () -> {
|
||||||
|
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||||
|
verifyTransactions(loanId.get(), //
|
||||||
|
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||||
|
transaction(19.35, "Accrual", "31 January 2023", 0, 0, 19.35, 0, 0, 0.0, 0.0));
|
||||||
|
});
|
||||||
|
runAt(repaymentPeriod1CloseDate, () -> {
|
||||||
|
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||||
|
verifyTransactions(loanId.get(), //
|
||||||
|
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||||
|
transaction(19.35, "Accrual", "31 January 2023", 0, 0, 19.35, 0, 0, 0.0, 0.0),
|
||||||
|
transaction(70.65, "Accrual", "01 February 2023", 0, 0, 0.65, 40, 30, 0.0, 0.0),
|
||||||
|
transaction(90.0, "Accrual Activity", "01 February 2023", 0, 0, 20.0, 40.0, 30.0, 0.0, 0.0));
|
||||||
|
|
||||||
|
});
|
||||||
|
runAt(repaymentPeriod1OneDayAfterCloseDate, () -> {
|
||||||
|
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
|
||||||
|
verifyTransactions(loanId.get(), //
|
||||||
|
transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||||
|
transaction(19.35, "Accrual", "31 January 2023", 0, 0, 19.35, 0, 0, 0.0, 0.0),
|
||||||
|
transaction(70.65, "Accrual", "01 February 2023", 0, 0, 0.65, 40, 30, 0.0, 0.0),
|
||||||
|
transaction(90.0, "Accrual Activity", "01 February 2023", 1000, 0, 20.0, 40.0, 30.0, 0.0, 0.0),
|
||||||
|
transaction(0.71, "Accrual", "02 February 2023", 0, 0, 0.71, 0, 0, 0.0, 0.0));
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chargeFee(Long loanId, Double amount, String dueDate) {
|
||||||
|
PostChargesResponse feeCharge = chargesHelper.createCharges(new PostChargesRequest().penalty(false).amount(9.0)
|
||||||
|
.chargeCalculationType(ChargeCalculationType.FLAT.getValue()).chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
||||||
|
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
||||||
|
.name(Utils.randomStringGenerator("FEE_" + Calendar.getInstance().getTimeInMillis(), 5)).chargeAppliesTo(1).locale("en")
|
||||||
|
.active(true));
|
||||||
|
PostLoansLoanIdChargesResponse feeLoanChargeResult = loanTransactionHelper.addChargesForLoan(loanId,
|
||||||
|
new PostLoansLoanIdChargesRequest().chargeId(feeCharge.getResourceId()).dateFormat(DATETIME_PATTERN).locale("en")
|
||||||
|
.amount(amount).dueDate(dueDate));
|
||||||
|
assertNotNull(feeLoanChargeResult);
|
||||||
|
assertNotNull(feeLoanChargeResult.getResourceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chargePenalty(Long loanId, Double amount, String dueDate) {
|
||||||
|
PostChargesResponse penaltyCharge = chargesHelper.createCharges(new PostChargesRequest().penalty(true).amount(10.0)
|
||||||
|
.chargeCalculationType(ChargeCalculationType.FLAT.getValue()).chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE.getValue())
|
||||||
|
.chargePaymentMode(ChargePaymentMode.REGULAR.getValue()).currencyCode("USD")
|
||||||
|
.name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5)).chargeAppliesTo(1).locale("en")
|
||||||
|
.active(true));
|
||||||
|
PostLoansLoanIdChargesResponse penaltyLoanChargeResult = loanTransactionHelper.addChargesForLoan(loanId,
|
||||||
|
new PostLoansLoanIdChargesRequest().chargeId(penaltyCharge.getResourceId()).dateFormat(DATETIME_PATTERN).locale("en")
|
||||||
|
.amount(amount).dueDate(dueDate));
|
||||||
|
assertNotNull(penaltyLoanChargeResult);
|
||||||
|
assertNotNull(penaltyLoanChargeResult.getResourceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long createLoanProductAccountingAccrualPeriodicWithInterest() {
|
||||||
|
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||||
|
String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
|
||||||
|
String shortName = Utils.uniqueRandomStringGenerator("", 4);
|
||||||
|
return loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest().name(name).shortName(shortName)
|
||||||
|
.description("Test loan description").currencyCode("USD").digitsAfterDecimal(2).daysInYearType(1).daysInMonthType(1)
|
||||||
|
.interestRecalculationCompoundingMethod(0).recalculationRestFrequencyType(1).rescheduleStrategyMethod(1)
|
||||||
|
.recalculationRestFrequencyInterval(0).isInterestRecalculationEnabled(false).interestRateFrequencyType(2).locale("en_GB")
|
||||||
|
.numberOfRepayments(4).repaymentFrequencyType(2L).interestRatePerPeriod(2.0).repaymentEvery(1).minPrincipal(100.0)
|
||||||
|
.principal(1000.0).maxPrincipal(10000000.0).amortizationType(1).interestType(1).interestCalculationPeriodType(1)
|
||||||
|
.dateFormat("dd MMMM yyyy").transactionProcessingStrategyCode(DEFAULT_STRATEGY).accountingRule(3)
|
||||||
|
.enableAccrualActivityPosting(true).fundSourceAccountId(fundSource.getAccountID().longValue())//
|
||||||
|
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
|
||||||
|
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
|
||||||
|
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromFeeAccountId(feeIncomeAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromPenaltyAccountId(feeIncomeAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromRecoveryAccountId(recoveriesAccount.getAccountID().longValue())//
|
||||||
|
.writeOffAccountId(writtenOffAccount.getAccountID().longValue())//
|
||||||
|
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
|
||||||
|
.receivableInterestAccountId(interestReceivableAccount.getAccountID().longValue())//
|
||||||
|
.receivableFeeAccountId(feeReceivableAccount.getAccountID().longValue())//
|
||||||
|
.receivablePenaltyAccountId(penaltyReceivableAccount.getAccountID().longValue())//
|
||||||
|
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromGoodwillCreditInterestAccountId(goodwillIncomeAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromGoodwillCreditFeesAccountId(goodwillIncomeAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromGoodwillCreditPenaltyAccountId(goodwillIncomeAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromChargeOffInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
|
||||||
|
.chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
|
||||||
|
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
|
||||||
|
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
|
||||||
|
).getResourceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Long applyForLoanApplicationWithInterest(final Long clientID, final Long loanProductID, BigDecimal principal,
|
||||||
|
String applicationDisbursementDate) {
|
||||||
|
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||||
|
final PostLoansRequest loanRequest = new PostLoansRequest() //
|
||||||
|
.loanTermFrequency(4).locale("en_GB").loanTermFrequencyType(2).numberOfRepayments(4).repaymentFrequencyType(2)
|
||||||
|
.interestRatePerPeriod(BigDecimal.valueOf(2)).repaymentEvery(1).principal(principal).amortizationType(1).interestType(1)
|
||||||
|
.interestCalculationPeriodType(1).dateFormat("dd MMMM yyyy").transactionProcessingStrategyCode(DEFAULT_STRATEGY)
|
||||||
|
.loanType("individual").expectedDisbursementDate(applicationDisbursementDate).submittedOnDate(applicationDisbursementDate)
|
||||||
|
.clientId(clientID).productId(loanProductID);
|
||||||
|
return loanTransactionHelper.applyLoan(loanRequest).getLoanId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
@@ -550,6 +550,16 @@ public class ExternalEventConfigurationHelper {
|
|||||||
loanTransactionInterestPaymentWaiverPreBusinessEvent.put("enabled", false);
|
loanTransactionInterestPaymentWaiverPreBusinessEvent.put("enabled", false);
|
||||||
defaults.add(loanTransactionInterestPaymentWaiverPreBusinessEvent);
|
defaults.add(loanTransactionInterestPaymentWaiverPreBusinessEvent);
|
||||||
|
|
||||||
|
Map<String, Object> loanTransactionAccrualActivityPostBusinessEvent = new HashMap<>();
|
||||||
|
loanTransactionAccrualActivityPostBusinessEvent.put("type", "LoanTransactionAccrualActivityPostBusinessEvent");
|
||||||
|
loanTransactionAccrualActivityPostBusinessEvent.put("enabled", false);
|
||||||
|
defaults.add(loanTransactionAccrualActivityPostBusinessEvent);
|
||||||
|
|
||||||
|
Map<String, Object> loanTransactionAccrualActivityPreBusinessEvent = new HashMap<>();
|
||||||
|
loanTransactionAccrualActivityPreBusinessEvent.put("type", "LoanTransactionAccrualActivityPreBusinessEvent");
|
||||||
|
loanTransactionAccrualActivityPreBusinessEvent.put("enabled", false);
|
||||||
|
defaults.add(loanTransactionAccrualActivityPreBusinessEvent);
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user