This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new c18fead7f
FINERACT-1905-Charge-submitted-date-Accrual-entry-reverse-issue
c18fead7f is described below
commit c18fead7f3c80c9b9bef078044470cdfafaced02
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Fri May 19 13:28:20 2023 +0530
FINERACT-1905-Charge-submitted-date-Accrual-entry-reverse-issue
---
.../TemporaryConfigurationServiceContainer.java | 4 +
.../portfolio/loanaccount/domain/Loan.java | 14 ++--
...ccrualTransactionOnChargeSubmittedDateTest.java | 97 ++++++++++++++++++++++
3 files changed, 110 insertions(+), 5 deletions(-)
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/TemporaryConfigurationServiceContainer.java
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/TemporaryConfigurationServiceContainer.java
index d91c4b174..357a09aef 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/TemporaryConfigurationServiceContainer.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/TemporaryConfigurationServiceContainer.java
@@ -42,6 +42,10 @@ public class TemporaryConfigurationServiceContainer
implements InitializingBean
return
TemporaryConfigurationServiceContainer.STATIC_REF_CONFIGURATION_SERVICE.isExternalIdAutoGenerationEnabled();
}
+ public static String getAccrualDateConfigForCharge() {
+ return
TemporaryConfigurationServiceContainer.STATIC_REF_CONFIGURATION_SERVICE.getAccrualDateConfigForCharge();
+ }
+
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
@Override
public void afterPropertiesSet() throws Exception {
diff --git
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 3c7439b5f..d8413e9b4 100644
---
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -1322,17 +1322,21 @@ public class Loan extends
AbstractAuditableWithUTCDateTimeCustom {
private void applyPeriodicAccruals(final Collection<LoanTransaction>
accruals) {
List<LoanRepaymentScheduleInstallment> installments =
getRepaymentScheduleInstallments();
+ boolean isBasedOnSubmittedOnDate =
TemporaryConfigurationServiceContainer.getAccrualDateConfigForCharge()
+ .equalsIgnoreCase("submitted-date");
for (LoanRepaymentScheduleInstallment installment : installments) {
-
Money interest = Money.zero(getCurrency());
Money fee = Money.zero(getCurrency());
Money penality = Money.zero(getCurrency());
for (LoanTransaction loanTransaction : accruals) {
+ LocalDate transactionDateForRange = isBasedOnSubmittedOnDate
+ ?
loanTransaction.getLoanChargesPaid().stream().findFirst().get().getLoanCharge().getDueDate()
+ : loanTransaction.getTransactionDate();
boolean isInRange = installment.isFirstPeriod()
- ?
!loanTransaction.getTransactionDate().isBefore(installment.getFromDate())
- &&
!loanTransaction.getTransactionDate().isAfter(installment.getDueDate())
- :
loanTransaction.getTransactionDate().isAfter(installment.getFromDate())
- &&
!loanTransaction.getTransactionDate().isAfter(installment.getDueDate());
+ ?
!transactionDateForRange.isBefore(installment.getFromDate())
+ &&
!transactionDateForRange.isAfter(installment.getDueDate())
+ :
transactionDateForRange.isAfter(installment.getFromDate())
+ &&
!transactionDateForRange.isAfter(installment.getDueDate());
if (isInRange) {
interest =
interest.plus(loanTransaction.getInterestPortion(getCurrency()));
fee =
fee.plus(loanTransaction.getFeeChargesPortion(getCurrency()));
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccrualTransactionOnChargeSubmittedDateTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccrualTransactionOnChargeSubmittedDateTest.java
index 99beb0859..858e19349 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccrualTransactionOnChargeSubmittedDateTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccrualTransactionOnChargeSubmittedDateTest.java
@@ -521,6 +521,76 @@ public class
LoanAccrualTransactionOnChargeSubmittedDateTest {
}
}
+ @Test
+ public void
loanAccrualTransactionOnChargeSubmitted_multiple_disbursement_reversal_test_Loan_COB()
{
+ try {
+
+ final SchedulerJobHelper schedulerJobHelper = new
SchedulerJobHelper(requestSpec);
+ // Accounts oof periodic accrual
+ final Account assetAccount =
this.accountHelper.createAssetAccount();
+ final Account incomeAccount =
this.accountHelper.createIncomeAccount();
+ final Account expenseAccount =
this.accountHelper.createExpenseAccount();
+ final Account overpaymentAccount =
this.accountHelper.createLiabilityAccount();
+
+ // Set business date
+ LocalDate currentDate = LocalDate.of(2023, 03, 3);
+
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.TRUE);
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, currentDate);
+
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec,
this.responseSpec, "submitted-date");
+ // Loan ExternalId
+ String loanExternalIdStr = UUID.randomUUID().toString();
+
+ // Client and Loan account creation
+
+ final Integer clientId =
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+ final GetLoanProductsProductIdResponse
getLoanProductsProductResponse = createLoanProductMultipleDisbursements(
+ loanTransactionHelper, assetAccount, incomeAccount,
expenseAccount, overpaymentAccount);
+ assertNotNull(getLoanProductsProductResponse);
+
+ final Integer loanId =
createLoanAccountMultipleRepaymentsDisbursement(clientId,
getLoanProductsProductResponse.getId(),
+ loanExternalIdStr);
+
+ loanTransactionHelper.disburseLoanWithTransactionAmount("03 March
2023", loanId, "1000");
+
+ // Add Charge Penalty
+ Integer penalty = ChargesHelper.createCharges(requestSpec,
responseSpec,
+
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
"10", true));
+
+ LocalDate targetDate = LocalDate.of(2023, 3, 9);
+ final String penaltyCharge1AddedDate =
dateFormatter.format(targetDate);
+
+ Integer penalty1LoanChargeId =
this.loanTransactionHelper.addChargesForLoan(loanId,
+
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
penaltyCharge1AddedDate, "10"));
+
+ assertNotNull(penalty1LoanChargeId);
+
+ // Run cob job for business date + 1
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, currentDate.plusDays(1));
+
+ final String jobName = "Loan COB";
+ schedulerJobHelper.executeAndAwaitJob(jobName);
+
+ // verify accrual transaction created for charges create date
+ checkAccrualTransaction(currentDate, 0.0f, 0.0f, 10.0f, loanId);
+
+ // Set business date
+ LocalDate futureDate = LocalDate.of(2023, 03, 4);
+
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec,
BusinessDateType.BUSINESS_DATE, futureDate);
+
+ loanTransactionHelper.disburseLoanWithTransactionAmount("04 March
2023", loanId, "300");
+
+ // verify accrual transaction exists with same date,amount and is
not reversed by regeneration of repayment
+ // schedule
+ checkAccrualTransaction(currentDate, 0.0f, 0.0f, 10.0f, loanId);
+
+ } finally {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec,
responseSpec, Boolean.FALSE);
+
GlobalConfigurationHelper.updateChargeAccrualDateConfiguration(this.requestSpec,
this.responseSpec, "due-date");
+ }
+ }
+
private void
checkAccrualTransactionsForMultipleRepaymentSchedulesChargeDueDate(LocalDate
transactionDate, Integer loanId) {
ArrayList<HashMap> transactions = (ArrayList<HashMap>)
loanTransactionHelper.getLoanTransactions(this.requestSpec,
this.responseSpec, loanId);
@@ -591,6 +661,33 @@ public class
LoanAccrualTransactionOnChargeSubmittedDateTest {
return loanTransactionHelper.getLoanProduct(loanProductId);
}
+ private GetLoanProductsProductIdResponse
createLoanProductMultipleDisbursements(final LoanTransactionHelper
loanTransactionHelper,
+ final Account... accounts) {
+
+ final String loanProductJSON = new
LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+
.withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance()
+
.withAccountingRulePeriodicAccrual(accounts).withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30")
+ .withDaysInYear("365").withMoratorium("0",
"0").withMultiDisburse().withDisallowExpectedDisbursements(true).build(null);
+ final Integer loanProductId =
loanTransactionHelper.getLoanProductId(loanProductJSON);
+ return loanTransactionHelper.getLoanProduct(loanProductId);
+ }
+
+ private Integer createLoanAccountMultipleRepaymentsDisbursement(final
Integer clientID, final Long loanProductID,
+ final String externalId) {
+
+ String loanApplicationJSON = new
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("30")
+
.withLoanTermFrequencyAsDays().withNumberOfRepayments("10").withRepaymentEveryAfter("3").withRepaymentFrequencyTypeAsDays()
+
.withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
+
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate("03
March 2023")
+ .withSubmittedOnDate("03 March
2023").withLoanType("individual").withExternalId(externalId)
+ .build(clientID.toString(), loanProductID.toString(), null);
+
+ final Integer loanId =
loanTransactionHelper.getLoanId(loanApplicationJSON);
+ loanTransactionHelper.approveLoan("03 March 2023", "1000", loanId,
null);
+ return loanId;
+ }
+
private Integer createLoanAccountMultipleRepayments(final Integer
clientID, final Long loanProductID, final String externalId) {
String loanApplicationJSON = new
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("30")