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")

Reply via email to