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 0a9657b77 FINERACT-1806: Stop recognizing income after charge-off
0a9657b77 is described below

commit 0a9657b774ad3c6bf5320d2f47bf5c338076ff9e
Author: Adam Saghy <[email protected]>
AuthorDate: Mon Jul 17 18:26:20 2023 +0200

    FINERACT-1806: Stop recognizing income after charge-off
---
 .../portfolio/loanaccount/domain/Loan.java         |   4 +-
 .../domain/LoanAccountDomainServiceJpa.java        |   5 +-
 ...LoanScheduleCalculationPlatformServiceImpl.java |   3 +-
 .../service/LoanReadPlatformServiceImpl.java       |  14 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   5 +-
 .../LoanChargeOffAccountingTest.java               | 166 ++++++++++++++++++++-
 6 files changed, 177 insertions(+), 20 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index dd9cd2bd1..60da8b625 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -3475,7 +3475,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
 
     private void processIncomeAccrualTransactionOnLoanClosure() {
         if (this.loanInterestRecalculationDetails != null && 
this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
-                && this.getStatus().isClosedObligationsMet()) {
+                && this.getStatus().isClosedObligationsMet() && !isNpa() && 
!isChargedOff()) {
 
             ExternalId externalId = ExternalId.empty();
             boolean isExternalIdAutoGenerationEnabled = 
TemporaryConfigurationServiceContainer.isExternalIdAutoGenerationEnabled();
@@ -5747,7 +5747,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
 
     private LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO 
generatorDTO) {
 
-        if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() 
|| isNpa) {
+        if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() 
|| isNpa || isChargedOff()) {
             return null;
         }
         final InterestMethod interestMethod = 
this.loanRepaymentScheduleDetail.getInterestMethod();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index 2270c22fb..807342ad9 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -584,7 +584,7 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
     public void recalculateAccruals(Loan loan, boolean 
isInterestCalculationHappened) {
         LocalDate accruedTill = loan.getAccruedTill();
         if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || 
!isInterestCalculationHappened || accruedTill == null || loan.isNpa()
-                || !loan.getStatus().isActive()) {
+                || !loan.getStatus().isActive() || loan.isChargedOff()) {
             return;
         }
 
@@ -889,7 +889,8 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
         if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()
                 // to avoid collision with 
processIncomeAccrualTransactionOnLoanClosure()
                 && !(loan.getLoanInterestRecalculationDetails() != null
-                        && 
loan.getLoanInterestRecalculationDetails().isCompoundingToBePostedAsTransaction()))
 {
+                        && 
loan.getLoanInterestRecalculationDetails().isCompoundingToBePostedAsTransaction())
+                && !loan.isNpa() && !loan.isChargedOff()) {
 
             MonetaryCurrency currency = loan.getCurrency();
             Money interestPortion = Money.zero(currency);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
index 9c975adcd..65d8206b5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
@@ -148,7 +148,8 @@ public class LoanScheduleCalculationPlatformServiceImpl 
implements LoanScheduleC
         final LoanRepaymentScheduleTransactionProcessor 
loanRepaymentScheduleTransactionProcessor = 
loanRepaymentScheduleTransactionProcessorFactory
                 .determineProcessor(loan.transactionProcessingStrategy());
 
-        if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() 
|| loan.isNpa() || !loan.getStatus().isActive()
+        if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() 
|| loan.isNpa() || loan.isChargedOff()
+                || !loan.getStatus().isActive()
                 || 
!loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor())
 {
             return;
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 710fa04fd..ef0b1a2cf 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -1768,7 +1768,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 .append(" and (((ls.fee_charges_amount <> 
COALESCE(ls.accrual_fee_charges_derived, 0))")
                 .append(" or ( ls.penalty_charges_amount <> 
COALESCE(ls.accrual_penalty_charges_derived, 0))")
                 .append(" or ( ls.interest_amount <> 
COALESCE(ls.accrual_interest_derived, 0)))")
-                .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and loan.is_npa=false and ls.duedate <= :currentDate) 
");
+                .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and loan.is_npa=false and loan.is_charged_off = false 
and ls.duedate <= :currentDate) ");
 
         if (organisationStartDate != null) {
             sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
@@ -1793,7 +1793,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 .append(" and (((ls.fee_charges_amount <> 
COALESCE(ls.accrual_fee_charges_derived, 0))")
                 .append(" or ( ls.penalty_charges_amount <> 
COALESCE(ls.accrual_penalty_charges_derived, 0))")
                 .append(" or ( ls.interest_amount <> 
COALESCE(ls.accrual_interest_derived, 0)))")
-                .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and loan.is_npa=false) ");
+                .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and loan.is_npa=false and loan.is_charged_off = 
false) ");
 
         if (organisationStartDate != null) {
             sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
@@ -1830,7 +1830,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 .append(" or (ls.penalty_charges_amount <> 
COALESCE(ls.accrual_penalty_charges_derived, 0))")
                 .append(" or (ls.interest_amount <> 
COALESCE(ls.accrual_interest_derived, 0)))")
                 .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or 
loan.closedon_date is null)")
-                .append(" and loan.is_npa=false and (ls.duedate <= :tillDate 
or (ls.duedate > :tillDate and ls.fromdate < :tillDate)")
+                .append(" and loan.is_npa=false and loan.is_charged_off = 
false and (ls.duedate <= :tillDate or (ls.duedate > :tillDate and ls.fromdate < 
:tillDate)")
                 .append(" or (ls.installment = 1 and ls.fromdate = 
:tillDate))) ");
         Map<String, Object> paramMap = new HashMap<>(5);
         if (organisationStartDate != null) {
@@ -1859,7 +1859,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 .append(" or (ls.penalty_charges_amount <> 
COALESCE(ls.accrual_penalty_charges_derived, 0))")
                 .append(" or (ls.interest_amount <> 
COALESCE(ls.accrual_interest_derived, 0)))")
                 .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or 
loan.closedon_date is null)")
-                .append(" and loan.is_npa=false)");
+                .append(" and loan.is_npa=false and loan.is_charged_off = 
false)");
         Map<String, Object> paramMap = new HashMap<>(5);
         if (organisationStartDate != null) {
             sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
@@ -2058,7 +2058,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
         sqlBuilder.append(" left join  m_floating_rates bfr on  
bfr.is_base_lending_rate = true");
         sqlBuilder.append(" left join  m_floating_rates_periods bfrp on  
bfr.id = bfrp.floating_rates_id and bfrp.created_date >= ?");
         sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
-        sqlBuilder.append(" and ml.is_npa = false and dd.is_reversed = false 
");
+        sqlBuilder.append(" and ml.is_npa = false and loan.is_charged_off = 
false and dd.is_reversed = false ");
         sqlBuilder.append(" and ((");
         sqlBuilder.append("ml.interest_recalculation_enabled = true ");
         sqlBuilder.append(" and (ml.interest_recalcualated_on is null or 
ml.interest_recalcualated_on <> ?)");
@@ -2106,7 +2106,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
         sqlBuilder.append(" left join  m_floating_rates bfr on  
bfr.is_base_lending_rate = true");
         sqlBuilder.append(" left join  m_floating_rates_periods bfrp on  
bfr.id = bfrp.floating_rates_id and bfrp.created_date >= ?");
         sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
-        sqlBuilder.append(" and ml.is_npa = false and dd.is_reversed = false 
");
+        sqlBuilder.append(" and ml.is_npa = false and loan.is_charged_off = 
false and dd.is_reversed = false ");
         sqlBuilder.append(" and ((");
         sqlBuilder.append("ml.interest_recalculation_enabled = true ");
         sqlBuilder.append(" and (ml.interest_recalcualated_on is null or 
ml.interest_recalcualated_on <> ? )");
@@ -2397,7 +2397,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 .append(" inner join m_loan_repayment_schedule as repsch on 
repsch.loan_id = loan.id ")
                 .append(" inner join 
m_loan_interest_recalculation_additional_details as adddet on 
adddet.loan_repayment_schedule_id = repsch.id ")
                 .append(" left join m_loan_transaction as trans on 
(trans.is_reversed <> true and trans.transaction_type_enum = 19 and 
trans.loan_id = loan.id and trans.transaction_date = adddet.effective_date) ")
-                .append(" where loan.loan_status_id = 300 ").append(" and 
loan.is_npa = false ")
+                .append(" where loan.loan_status_id = 300 ").append(" and 
loan.is_npa = false and loan.is_charged_off = false ")
                 .append(" and adddet.effective_date is not null ").append(" 
and trans.transaction_date is null ")
                 .append(" and adddet.effective_date < ? ");
         try {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index d55b3d2a9..79eb8005c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -2749,10 +2749,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
             throw new 
GeneralPlatformDomainRuleException("error.msg.loan.transaction.cannot.be.a.future.date",
 errorMessage,
                     transactionDate);
         }
-        if (loan.isInterestBearing()) {
-            throw new 
GeneralPlatformDomainRuleException("error.msg.loan.is.interest.bearing",
-                    "Loan: " + loanId + " Charge-off is not allowed. Loan 
Account is interest bearing", loanId);
-        }
+
         businessEventNotifierService.notifyPreBusinessEvent(new 
LoanChargeOffPreBusinessEvent(loan));
 
         if (command.hasParameter(LoanApiConstants.chargeOffReasonIdParamName)) 
{
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
index 19da79d47..81462b81e 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.integrationtests;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.restassured.builder.RequestSpecBuilder;
@@ -28,6 +29,7 @@ import io.restassured.specification.ResponseSpecification;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
+import java.util.List;
 import java.util.UUID;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
@@ -35,18 +37,23 @@ import 
org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import org.apache.fineract.client.models.PutLoansLoanIdResponse;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.accounting.Account;
 import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
 import org.apache.fineract.integrationtests.common.accounting.JournalEntry;
 import 
org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
+import 
org.apache.fineract.integrationtests.common.accounting.PeriodicAccrualAccountingHelper;
 import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.apache.fineract.integrationtests.common.system.CodeHelper;
+import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -55,11 +62,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
 public class LoanChargeOffAccountingTest {
 
     private ResponseSpecification responseSpec;
-    private ResponseSpecification responseSpec403;
     private RequestSpecification requestSpec;
     private ClientHelper clientHelper;
     private LoanTransactionHelper loanTransactionHelper;
-    private LoanTransactionHelper loanTransactionHelperValidationError;
     private JournalEntryHelper journalEntryHelper;
     private AccountHelper accountHelper;
     private Account assetAccount;
@@ -67,16 +72,17 @@ public class LoanChargeOffAccountingTest {
     private Account expenseAccount;
     private Account overpaymentAccount;
     private DateTimeFormatter dateFormatter = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
+    private InlineLoanCOBHelper inlineLoanCOBHelper;
+    private PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper;
 
     @BeforeEach
     public void setup() {
         Utils.initializeRESTAssured();
         this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
         this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.requestSpec.header("Fineract-Platform-TenantId", "default");
         this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
-        this.responseSpec403 = new 
ResponseSpecBuilder().expectStatusCode(403).build();
         this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.loanTransactionHelperValidationError = new 
LoanTransactionHelper(this.requestSpec, new ResponseSpecBuilder().build());
         this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
         this.assetAccount = this.accountHelper.createAssetAccount();
         this.incomeAccount = this.accountHelper.createIncomeAccount();
@@ -84,6 +90,8 @@ public class LoanChargeOffAccountingTest {
         this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
         this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
         this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.inlineLoanCOBHelper = new InlineLoanCOBHelper(this.requestSpec, 
this.responseSpec);
+        this.periodicAccrualAccountingHelper = new 
PeriodicAccrualAccountingHelper(this.requestSpec, this.responseSpec);
     }
 
     @Test
@@ -679,6 +687,108 @@ public class LoanChargeOffAccountingTest {
                 new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
     }
 
+    @Test
+    public void noIncomeRecognitionAfterChargeOff() {
+        try {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 5));
+            // Loan ExternalId
+            String loanExternalIdStr = UUID.randomUUID().toString();
+
+            final Integer loanProductId = 
this.createLoanProductWithInterestRecalculation(assetAccount, incomeAccount, 
expenseAccount,
+                    overpaymentAccount);
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+            final Integer loanId = 
this.createLoanEntityWithEntitiesForTestResceduleWithLatePayment(clientId, 
loanProductId);
+
+            // apply charges
+            Integer feeCharge = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+            LocalDate targetDate = LocalDate.of(2022, 9, 5);
+            final String feeCharge1AddedDate = 
dateFormatter.format(targetDate);
+            Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+            // apply penalty
+            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
+
+            final String penaltyCharge1AddedDate = 
dateFormatter.format(targetDate);
+
+            Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 6));
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+            
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
+            
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
+            assertEquals(2, loanDetails.getTransactions().size());
+
+            // set loan as chargeoff
+            String randomText = Utils.randomStringGenerator("en", 5) + 
Utils.randomNumberGenerator(6)
+                    + Utils.randomStringGenerator("is", 5);
+            Integer chargeOffReasonId = 
CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
+            String transactionExternalId = UUID.randomUUID().toString();
+            this.loanTransactionHelper.chargeOffLoan((long) loanId,
+                    new 
PostLoansLoanIdTransactionsRequest().transactionDate("6 September 
2020").locale("en").dateFormat("dd MMMM yyyy")
+                            
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+            loanDetails = this.loanTransactionHelper.getLoanDetails((long) 
loanId);
+            assertTrue(loanDetails.getStatus().getActive());
+            assertTrue(loanDetails.getChargedOff());
+
+            // no accrual
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 7));
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+            
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
+            
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
+            
assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff());
+            assertEquals(3, loanDetails.getTransactions().size());
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 8));
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
+            
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(dateFormatter.format(LocalDate.of(2020,
 9, 8)));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+            
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
+            
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
+            
assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff());
+            assertEquals(3, loanDetails.getTransactions().size());
+
+            loanTransactionHelper.undoChargeOffLoan((long) loanId, new 
PostLoansLoanIdTransactionsRequest());
+            // generate accrual again
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 9));
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+            
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
+            
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
+            
assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff());
+            
assertTrue(loanDetails.getTransactions().get(3).getType().getAccrual());
+            assertEquals(4, loanDetails.getTransactions().size());
+
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 9, 10));
+
+            this.loanTransactionHelper.chargeOffLoan((long) loanId,
+                    new 
PostLoansLoanIdTransactionsRequest().transactionDate("10 September 
2020").locale("en").dateFormat("dd MMMM yyyy")
+                            .chargeOffReasonId((long) chargeOffReasonId));
+
+            loanTransactionHelper.makeLoanRepayment(loanId.longValue(), new 
PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy")
+                    .transactionDate("10 September 
2020").locale("en").transactionAmount(15825.23));
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+            
assertTrue(loanDetails.getTransactions().get(0).getType().getDisbursement());
+            
assertTrue(loanDetails.getTransactions().get(1).getType().getAccrual());
+            
assertTrue(loanDetails.getTransactions().get(2).getType().getChargeoff());
+            
assertTrue(loanDetails.getTransactions().get(3).getType().getAccrual());
+            
assertTrue(loanDetails.getTransactions().get(4).getType().getChargeoff());
+            
assertTrue(loanDetails.getTransactions().get(5).getType().getRepayment());
+            assertEquals(6, loanDetails.getTransactions().size());
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+    }
+
     private Integer createLoanAccount(final Integer clientID, final Integer 
loanProductID, final String externalId) {
 
         String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
@@ -705,6 +815,34 @@ public class LoanChargeOffAccountingTest {
         return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
     }
 
+    private Integer createLoanProductWithInterestRecalculation(final 
Account... accounts) {
+        final String interestRecalculationCompoundingMethod = 
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE;
+        final String rescheduleStrategyMethod = 
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS;
+        final String recalculationRestFrequencyType = 
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY;
+        final String recalculationRestFrequencyInterval = "0";
+        final String preCloseInterestCalculationStrategy = 
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE;
+        final String recalculationCompoundingFrequencyType = null;
+        final String recalculationCompoundingFrequencyInterval = null;
+        final Integer recalculationCompoundingFrequencyOnDayType = null;
+        final Integer recalculationCompoundingFrequencyDayOfWeekType = null;
+        final Integer recalculationRestFrequencyOnDayType = null;
+        final Integer recalculationRestFrequencyDayOfWeekType = null;
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("100000.00").withNumberOfRepayments("12")
+                
.withinterestRatePerPeriod("18").withInterestRateFrequencyTypeAsYear().withInterestTypeAsDecliningBalance()
+                .withInterestCalculationPeriodTypeAsDays()
+                
.withInterestRecalculationDetails(interestRecalculationCompoundingMethod, 
rescheduleStrategyMethod,
+                        preCloseInterestCalculationStrategy)
+                
.withInterestRecalculationRestFrequencyDetails(recalculationRestFrequencyType, 
recalculationRestFrequencyInterval,
+                        recalculationRestFrequencyOnDayType, 
recalculationRestFrequencyDayOfWeekType)
+                
.withInterestRecalculationCompoundingFrequencyDetails(recalculationCompoundingFrequencyType,
+                        recalculationCompoundingFrequencyInterval, 
recalculationCompoundingFrequencyOnDayType,
+                        recalculationCompoundingFrequencyDayOfWeekType)
+                .withAccountingRulePeriodicAccrual(accounts).build(null);
+
+        return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+    }
+
     private Integer createLoanProductWithCashBasedAccounting(final Account... 
accounts) {
 
         final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("1000").withRepaymentAfterEvery("1")
@@ -715,4 +853,24 @@ public class LoanChargeOffAccountingTest {
         return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
     }
 
+    private Integer 
createLoanEntityWithEntitiesForTestResceduleWithLatePayment(Integer clientId, 
Integer loanProductId) {
+        String firstRepaymentDate = "02 September 2020";
+        String submittedDate = "02 September 2020";
+
+        final String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("15000").withLoanTermFrequency("12")
+                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
+                
.withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestCalculationPeriodTypeAsDays()
+                
.withInterestRatePerPeriod("12").withInterestTypeAsDecliningBalance().withSubmittedOnDate(submittedDate)
+                
.withExpectedDisbursementDate(submittedDate).withFirstRepaymentDate(firstRepaymentDate)
+                .withRepaymentStrategy(
+                        
LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY)
+                
.withinterestChargedFromDate(submittedDate).build(clientId.toString(), 
loanProductId.toString(), null);
+
+        Integer loanId = 
this.loanTransactionHelper.getLoanId(loanApplicationJSON);
+
+        this.loanTransactionHelper.approveLoan(submittedDate, loanId);
+        
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(submittedDate, 
loanId, "10000.00");
+        return loanId;
+    }
+
 }

Reply via email to