This is an automated email from the ASF dual-hosted git repository.

arnold 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 d2fe39362 FINERACT-1968-Advanced-Payment-Allocation-Charge-Off
d2fe39362 is described below

commit d2fe39362cafcf9edf104d6a98ce3ba64c5877ea
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Thu Oct 5 18:18:13 2023 +0530

    FINERACT-1968-Advanced-Payment-Allocation-Charge-Off
---
 ...dvancedPaymentScheduleTransactionProcessor.java |  21 +
 ...ChargeOffWithAdvancedPaymentAllocationTest.java | 759 +++++++++++++++++++++
 2 files changed, 780 insertions(+)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index 6a9c9d8a5..d51a8fb2f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -156,6 +156,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             case REPAYMENT, MERCHANT_ISSUED_REFUND, PAYOUT_REFUND, 
GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT, DOWN_PAYMENT,
                     WAIVE_INTEREST, RECOVERY_REPAYMENT ->
                 handleRepayment(loanTransaction, currency, installments, 
charges);
+            case CHARGE_OFF -> handleChargeOff(loanTransaction, currency, 
installments);
             // TODO: Cover rest of the transaction types
             default -> {
                 log.warn("Unhandled transaction processing for transaction 
type: {}", loanTransaction.getTypeOf());
@@ -363,6 +364,26 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         }
     }
 
+    private void handleChargeOff(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
+            List<LoanRepaymentScheduleInstallment> installments) {
+        loanTransaction.resetDerivedComponents();
+        // determine how much is outstanding total and breakdown for 
principal, interest and charges
+        Money principalPortion = Money.zero(currency);
+        Money interestPortion = Money.zero(currency);
+        Money feeChargesPortion = Money.zero(currency);
+        Money penaltychargesPortion = Money.zero(currency);
+        for (final LoanRepaymentScheduleInstallment currentInstallment : 
installments) {
+            if (currentInstallment.isNotFullyPaidOff()) {
+                principalPortion = 
principalPortion.plus(currentInstallment.getPrincipalOutstanding(currency));
+                interestPortion = 
interestPortion.plus(currentInstallment.getInterestOutstanding(currency));
+                feeChargesPortion = 
feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(currency));
+                penaltychargesPortion = 
penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
+            }
+        }
+
+        loanTransaction.updateComponentsAndTotal(principalPortion, 
interestPortion, feeChargesPortion, penaltychargesPortion);
+    }
+
     @AllArgsConstructor
     @Getter
     @Setter
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java
new file mode 100644
index 000000000..e6d32ab67
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java
@@ -0,0 +1,759 @@
+/**
+ * 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.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+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.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.fineract.client.models.AdvancedPaymentData;
+import org.apache.fineract.client.models.AllowAttributeOverrides;
+import org.apache.fineract.client.models.ChargeData;
+import org.apache.fineract.client.models.ChargeToGLAccountMapper;
+import 
org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
+import org.apache.fineract.client.models.GetLoanFeeToIncomeAccountMappings;
+import 
org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings;
+import org.apache.fineract.client.models.GetLoanTransactionRelation;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
+import org.apache.fineract.client.models.JournalEntryTransactionItem;
+import org.apache.fineract.client.models.PaymentAllocationOrder;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
+import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.client.models.PostPaymentTypesRequest;
+import org.apache.fineract.client.models.PostPaymentTypesResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
+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.JournalEntryHelper;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import org.apache.fineract.integrationtests.common.funds.FundsHelper;
+import org.apache.fineract.integrationtests.common.funds.FundsResourceHandler;
+import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanProductHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import 
org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.apache.fineract.integrationtests.common.system.CodeHelper;
+import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(LoanTestLifecycleExtension.class)
+public class LoanAccountChargeOffWithAdvancedPaymentAllocationTest {
+
+    private static final DateTimeFormatter DATE_FORMATTER = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private ClientHelper clientHelper;
+    private LoanTransactionHelper loanTransactionHelper;
+    private JournalEntryHelper journalEntryHelper;
+    private AccountHelper accountHelper;
+    private LoanProductHelper loanProductHelper;
+    private PaymentTypeHelper paymentTypeHelper;
+    // asset
+    private Account loansReceivable;
+    private Account interestFeeReceivable;
+    private Account suspenseAccount;
+    private Account fundReceivables;
+    // liability
+    private Account suspenseClearingAccount;
+    private Account overpaymentAccount;
+    // income
+    private Account interestIncome;
+    private Account feeIncome;
+    private Account feeChargeOff;
+    private Account recoveries;
+    private Account interestIncomeChargeOff;
+    // expense
+    private Account creditLossBadDebt;
+    private Account creditLossBadDebtFraud;
+    private Account writtenOff;
+    private Account goodwillExpenseAccount;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
+        this.loanProductHelper = new LoanProductHelper();
+        this.paymentTypeHelper = new PaymentTypeHelper();
+
+        // Asset
+        this.loansReceivable = this.accountHelper.createAssetAccount();
+        this.interestFeeReceivable = this.accountHelper.createAssetAccount();
+        this.suspenseAccount = this.accountHelper.createAssetAccount();
+        this.fundReceivables = this.accountHelper.createAssetAccount();
+
+        // Liability
+        this.suspenseClearingAccount = 
this.accountHelper.createLiabilityAccount();
+        this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
+
+        // income
+        this.interestIncome = this.accountHelper.createIncomeAccount();
+        this.feeIncome = this.accountHelper.createIncomeAccount();
+        this.feeChargeOff = this.accountHelper.createIncomeAccount();
+        this.recoveries = this.accountHelper.createIncomeAccount();
+        this.interestIncomeChargeOff = 
this.accountHelper.createIncomeAccount();
+
+        // expense
+        this.creditLossBadDebt = this.accountHelper.createExpenseAccount();
+        this.creditLossBadDebtFraud = 
this.accountHelper.createExpenseAccount();
+        this.writtenOff = this.accountHelper.createExpenseAccount();
+        this.goodwillExpenseAccount = 
this.accountHelper.createExpenseAccount();
+
+        this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
+        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+    }
+
+    // Charge-off accounting and balances
+    @Test
+    public void loanChargeOffWithAdvancedPaymentStrategyTest() {
+        String loanExternalIdStr = UUID.randomUUID().toString();
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // 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 = DATE_FORMATTER.format(targetDate);
+        Integer feeLoanChargeId = 
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 = 
DATE_FORMATTER.format(targetDate);
+
+        Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+        // make Repayment
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("9 September 2022").locale("en")
+                        .transactionAmount(10.0));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // 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();
+        PostLoansLoanIdTransactionsResponse chargeOffTransaction = 
this.loanTransactionHelper.chargeOffLoan((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().transactionDate("10 
September 2022").locale("en").dateFormat("dd MMMM yyyy")
+                        
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify amounts for charge-off transaction
+        verifyTransaction(LocalDate.of(2022, 9, 10), 1010.0f, 1000.0f, 0.0f, 
10.0f, 0.0f, loanId, "chargeoff");
+        // verify journal entries
+        GetJournalEntriesTransactionIdResponse journalEntriesForChargeOff = 
journalEntryHelper
+                .getJournalEntries("L" + 
chargeOffTransaction.getResourceId().toString());
+
+        assertNotNull(journalEntriesForChargeOff);
+
+        List<JournalEntryTransactionItem> journalEntries = 
journalEntriesForChargeOff.getPageItems();
+        assertEquals(4, journalEntries.size());
+        verifyJournalEntry(journalEntries.get(3), 1000.0, LocalDate.of(2022, 
9, 10), loansReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(2), 10.0, LocalDate.of(2022, 9, 
10), interestFeeReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(1), 1000.0, LocalDate.of(2022, 
9, 10), creditLossBadDebt, "DEBIT");
+        verifyJournalEntry(journalEntries.get(0), 10.0, LocalDate.of(2022, 9, 
10), feeChargeOff, "DEBIT");
+
+    }
+
+    // Reverse Replay of Charge-Off
+    @Test
+    public void loanChargeOffReverseReplayWithAdvancedPaymentStrategyTest() {
+        String loanExternalIdStr = UUID.randomUUID().toString();
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // 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 = DATE_FORMATTER.format(targetDate);
+        Integer feeLoanChargeId = 
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 = 
DATE_FORMATTER.format(targetDate);
+
+        Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+        // make Repayment
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("9 September 2022").locale("en")
+                        .transactionAmount(10.0));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // 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();
+        PostLoansLoanIdTransactionsResponse chargeOffTransaction = 
this.loanTransactionHelper.chargeOffLoan((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().transactionDate("10 
September 2022").locale("en").dateFormat("dd MMMM yyyy")
+                        
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify amounts for charge-off transaction
+        verifyTransaction(LocalDate.of(2022, 9, 10), 1010.0f, 1000.0f, 0.0f, 
10.0f, 0.0f, loanId, "chargeoff");
+
+        Long reversedAndReplayedTransactionId = 
chargeOffTransaction.getResourceId();
+
+        // reverse Repayment
+        loanTransactionHelper.reverseRepayment(loanId, 
repaymentTransaction.getResourceId().intValue(), "11 September 2022");
+
+        // verify chargeOffTransaction gets reverse replayed
+
+        GetLoansLoanIdTransactionsTransactionIdResponse 
getLoansTransactionResponse = loanTransactionHelper
+                .getLoanTransactionDetails((long) loanId, 
transactionExternalId);
+        assertNotNull(getLoansTransactionResponse);
+        assertNotNull(getLoansTransactionResponse.getTransactionRelations());
+
+        // test replayed relationship
+        GetLoanTransactionRelation transactionRelation = 
getLoansTransactionResponse.getTransactionRelations().iterator().next();
+        assertEquals(reversedAndReplayedTransactionId, 
transactionRelation.getToLoanTransaction());
+        assertEquals("REPLAYED", transactionRelation.getRelationType());
+
+        // verify amounts for charge-off transaction
+        verifyTransaction(LocalDate.of(2022, 9, 10), 1020.0f, 1000.0f, 0.0f, 
10.0f, 10.0f, loanId, "chargeoff");
+
+    }
+
+    // undo Charge-Off
+    @Test
+    public void loanUndoChargeOffTest() {
+        // Loan ExternalId
+        String loanExternalIdStr = UUID.randomUUID().toString();
+
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // make Repayment
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("6 September 2022").locale("en")
+                        .transactionAmount(100.0));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // 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();
+        loanTransactionHelper.chargeOffLoan((long) loanId, new 
PostLoansLoanIdTransactionsRequest().transactionDate("7 September 2022")
+                .locale("en").dateFormat("dd MMMM 
yyyy").externalId(transactionExternalId).chargeOffReasonId((long) 
chargeOffReasonId));
+
+        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // undo charge-off
+        String reverseTransactionExternalId = UUID.randomUUID().toString();
+        PostLoansLoanIdTransactionsResponse undoChargeOffTxResponse = 
loanTransactionHelper.undoChargeOffLoan((long) loanId,
+                new 
PostLoansLoanIdTransactionsRequest().reversalExternalId(reverseTransactionExternalId));
+        assertNotNull(undoChargeOffTxResponse);
+
+        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertFalse(loanDetails.getChargedOff());
+
+        GetLoansLoanIdTransactionsTransactionIdResponse 
chargeOffTransactionDetails = loanTransactionHelper
+                .getLoanTransactionDetails((long) loanId, 
transactionExternalId);
+        assertNotNull(chargeOffTransactionDetails);
+        assertTrue(chargeOffTransactionDetails.getManuallyReversed());
+        assertEquals(reverseTransactionExternalId, 
chargeOffTransactionDetails.getReversalExternalId());
+    }
+
+    // Backdated repayment transaction, Reverse replay of charge off
+    @Test
+    public void postChargeOffAddBackdatedTransactionAndReverseReplayTest() {
+        String loanExternalIdStr = UUID.randomUUID().toString();
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // 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 = DATE_FORMATTER.format(targetDate);
+        Integer feeLoanChargeId = 
loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+        // 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();
+        PostLoansLoanIdTransactionsResponse chargeOffTransaction = 
loanTransactionHelper.chargeOffLoan((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().transactionDate("14 
September 2022").locale("en").dateFormat("dd MMMM yyyy")
+                        
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+
+        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        Long reversedAndReplayedTransactionId = 
chargeOffTransaction.getResourceId();
+
+        // verify Journal Entries For ChargeOff Transaction
+        GetJournalEntriesTransactionIdResponse journalEntriesForChargeOff = 
journalEntryHelper
+                .getJournalEntries("L" + 
chargeOffTransaction.getResourceId().toString());
+
+        assertNotNull(journalEntriesForChargeOff);
+        List<JournalEntryTransactionItem> journalEntries = 
journalEntriesForChargeOff.getPageItems();
+        assertEquals(4, journalEntries.size());
+
+        verifyJournalEntry(journalEntries.get(3), 1000.0, LocalDate.of(2022, 
9, 14), loansReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(2), 10.0, LocalDate.of(2022, 9, 
14), interestFeeReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(1), 1000.0, LocalDate.of(2022, 
9, 14), creditLossBadDebt, "DEBIT");
+        verifyJournalEntry(journalEntries.get(0), 10.0, LocalDate.of(2022, 9, 
14), feeChargeOff, "DEBIT");
+
+        // make Repayment before chargeoff date
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("7 September 2022").locale("en")
+                        .transactionAmount(100.0));
+
+        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries for Repayment transaction
+
+        GetJournalEntriesTransactionIdResponse journalEntriesForRepayment = 
journalEntryHelper
+                .getJournalEntries("L" + 
repaymentTransaction.getResourceId().toString());
+        assertNotNull(journalEntriesForRepayment);
+
+        journalEntries = journalEntriesForRepayment.getPageItems();
+        assertEquals(3, journalEntries.size());
+
+        verifyJournalEntry(journalEntries.get(2), 90.0, LocalDate.of(2022, 9, 
7), loansReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(1), 10.0, LocalDate.of(2022, 9, 
7), interestFeeReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(0), 100.0, LocalDate.of(2022, 9, 
7), suspenseClearingAccount, "DEBIT");
+
+        // verify reverse replay of Charge-Off
+
+        GetLoansLoanIdTransactionsTransactionIdResponse 
getLoansTransactionResponse = loanTransactionHelper
+                .getLoanTransactionDetails((long) loanId, 
transactionExternalId);
+        assertNotNull(getLoansTransactionResponse);
+        assertNotNull(getLoansTransactionResponse.getTransactionRelations());
+
+        // test replayed relationship
+        GetLoanTransactionRelation transactionRelation = 
getLoansTransactionResponse.getTransactionRelations().iterator().next();
+        assertEquals(reversedAndReplayedTransactionId, 
transactionRelation.getToLoanTransaction());
+        assertEquals("REPLAYED", transactionRelation.getRelationType());
+
+        // verify amounts for charge-off transaction
+        verifyTransaction(LocalDate.of(2022, 9, 14), 910.0f, 910.0f, 0.0f, 
0.0f, 0.0f, loanId, "chargeoff");
+
+        // make Repayment after chargeoff date
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("15 September 2022").locale("en")
+                        .transactionAmount(100.0));
+
+        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries for Repayment transaction
+        journalEntriesForRepayment = journalEntryHelper.getJournalEntries("L" 
+ repaymentTransaction_1.getResourceId().toString());
+
+        assertNotNull(journalEntriesForRepayment);
+
+        journalEntries = journalEntriesForRepayment.getPageItems();
+        assertEquals(2, journalEntries.size());
+
+        verifyJournalEntry(journalEntries.get(1), 100.0, LocalDate.of(2022, 9, 
15), recoveries, "CREDIT");
+        verifyJournalEntry(journalEntries.get(0), 100.0, LocalDate.of(2022, 9, 
15), suspenseClearingAccount, "DEBIT");
+    }
+
+    // Repayment before charge off on charge off date, reverse replay of 
charge off
+    @Test
+    public void transactionOnChargeOffDateReverseTest() {
+        String loanExternalIdStr = UUID.randomUUID().toString();
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // 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 = DATE_FORMATTER.format(targetDate);
+        Integer feeLoanChargeId = 
loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+        // make Repayment before charge-off on charge off date
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("7 September 2022").locale("en")
+                        .transactionAmount(100.0));
+
+        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // verify Journal Entries for Repayment transaction
+        GetJournalEntriesTransactionIdResponse journalEntriesForRepayment = 
journalEntryHelper
+                .getJournalEntries("L" + 
repaymentTransaction.getResourceId().toString());
+
+        assertNotNull(journalEntriesForRepayment);
+
+        List<JournalEntryTransactionItem> journalEntries = 
journalEntriesForRepayment.getPageItems();
+        assertEquals(3, journalEntries.size());
+
+        verifyJournalEntry(journalEntries.get(2), 90.0, LocalDate.of(2022, 9, 
7), loansReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(1), 10.0, LocalDate.of(2022, 9, 
7), interestFeeReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(0), 100.0, LocalDate.of(2022, 9, 
7), suspenseClearingAccount, "DEBIT");
+
+        // 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();
+        PostLoansLoanIdTransactionsResponse chargeOffTransaction = 
loanTransactionHelper.chargeOffLoan((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().transactionDate("7 
September 2022").locale("en").dateFormat("dd MMMM yyyy")
+                        
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+
+        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        Long reversedAndReplayedTransactionId = 
chargeOffTransaction.getResourceId();
+
+        // verify Journal Entries For ChargeOff Transaction
+        GetJournalEntriesTransactionIdResponse journalEntriesForChargeOff = 
journalEntryHelper
+                .getJournalEntries("L" + 
chargeOffTransaction.getResourceId().toString());
+
+        assertNotNull(journalEntriesForChargeOff);
+        journalEntries = journalEntriesForChargeOff.getPageItems();
+        assertEquals(2, journalEntries.size());
+
+        verifyJournalEntry(journalEntries.get(1), 910.0, LocalDate.of(2022, 9, 
7), loansReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(0), 910.0, LocalDate.of(2022, 9, 
7), creditLossBadDebt, "DEBIT");
+
+        // reverse Repayment
+        loanTransactionHelper.reverseRepayment(loanId, 
repaymentTransaction.getResourceId().intValue(), "7 September 2022");
+        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries for Reversed Repayment transaction
+        journalEntriesForRepayment = journalEntryHelper.getJournalEntries("L" 
+ repaymentTransaction.getResourceId().toString());
+        assertNotNull(journalEntriesForRepayment);
+
+        journalEntries = journalEntriesForRepayment.getPageItems();
+        assertEquals(6, journalEntries.size());
+
+        verifyJournalEntry(journalEntries.get(5), 90.0, LocalDate.of(2022, 9, 
7), loansReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(4), 10.0, LocalDate.of(2022, 9, 
7), interestFeeReceivable, "CREDIT");
+        verifyJournalEntry(journalEntries.get(3), 100.0, LocalDate.of(2022, 9, 
7), suspenseClearingAccount, "DEBIT");
+        verifyJournalEntry(journalEntries.get(2), 90.0, LocalDate.of(2022, 9, 
7), loansReceivable, "DEBIT");
+        verifyJournalEntry(journalEntries.get(1), 10.0, LocalDate.of(2022, 9, 
7), interestFeeReceivable, "DEBIT");
+        verifyJournalEntry(journalEntries.get(0), 100.0, LocalDate.of(2022, 9, 
7), suspenseClearingAccount, "CREDIT");
+
+        // verify reverse replay of Charge-Off
+
+        GetLoansLoanIdTransactionsTransactionIdResponse 
getLoansTransactionResponse = loanTransactionHelper
+                .getLoanTransactionDetails((long) loanId, 
transactionExternalId);
+        assertNotNull(getLoansTransactionResponse);
+        assertNotNull(getLoansTransactionResponse.getTransactionRelations());
+
+        // test replayed relationship
+        GetLoanTransactionRelation transactionRelation = 
getLoansTransactionResponse.getTransactionRelations().iterator().next();
+        assertEquals(reversedAndReplayedTransactionId, 
transactionRelation.getToLoanTransaction());
+        assertEquals("REPLAYED", transactionRelation.getRelationType());
+
+        // verify amounts for charge-off transaction
+        verifyTransaction(LocalDate.of(2022, 9, 7), 1010.0f, 1000.0f, 0.0f, 
10.0f, 0.0f, loanId, "chargeoff");
+
+    }
+
+    private void verifyJournalEntry(JournalEntryTransactionItem 
journalEntryTransactionItem, Double amount, LocalDate entryDate,
+            Account account, String type) {
+        assertEquals(amount, journalEntryTransactionItem.getAmount());
+        assertEquals(entryDate, 
journalEntryTransactionItem.getTransactionDate());
+        assertEquals(account.getAccountID().longValue(), 
journalEntryTransactionItem.getGlAccountId().longValue());
+        assertEquals(type, 
journalEntryTransactionItem.getEntryType().getValue());
+    }
+
+    private void verifyTransaction(final LocalDate transactionDate, final 
Float transactionAmount, final Float principalPortion,
+            final Float interestPortion, final Float feePortion, final Float 
penaltyPortion, final Integer loanID,
+            final String transactionOfType) {
+        ArrayList<HashMap> transactions = (ArrayList<HashMap>) 
loanTransactionHelper.getLoanTransactions(this.requestSpec,
+                this.responseSpec, loanID);
+        boolean isTransactionFound = false;
+        for (int i = 0; i < transactions.size(); i++) {
+            HashMap transactionType = (HashMap) 
transactions.get(i).get("type");
+            boolean isTransaction = (Boolean) 
transactionType.get(transactionOfType);
+
+            if (isTransaction) {
+                ArrayList<Integer> transactionDateAsArray = 
(ArrayList<Integer>) transactions.get(i).get("date");
+                LocalDate transactionEntryDate = 
LocalDate.of(transactionDateAsArray.get(0), transactionDateAsArray.get(1),
+                        transactionDateAsArray.get(2));
+
+                if (transactionDate.isEqual(transactionEntryDate)) {
+                    isTransactionFound = true;
+                    assertEquals(transactionAmount, 
Float.valueOf(String.valueOf(transactions.get(i).get("amount"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(principalPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("principalPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(interestPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("interestPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(feePortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("feeChargesPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(penaltyPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("penaltyChargesPortion"))),
+                            "Mismatch in transaction amounts");
+                    break;
+                }
+            }
+        }
+        assertTrue(isTransactionFound, "No Transaction entries are posted");
+    }
+
+    private Integer createLoanAccount(final Integer clientID, final Integer 
loanProductID, final String externalId) {
+
+        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("30")
+                
.withLoanTermFrequencyAsDays().withNumberOfRepayments("1").withRepaymentEveryAfter("30").withRepaymentFrequencyTypeAsDays()
+                
.withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
+                
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate("03
 September 2022")
+                .withSubmittedOnDate("01 September 
2022").withLoanType("individual").withExternalId(externalId)
+                
.withRepaymentStrategy("advanced-payment-allocation-strategy").build(clientID.toString(),
 loanProductID.toString(), null);
+
+        final Integer loanId = 
loanTransactionHelper.getLoanId(loanApplicationJSON);
+        loanTransactionHelper.approveLoan("02 September 2022", "1000", loanId, 
null);
+        loanTransactionHelper.disburseLoanWithTransactionAmount("03 September 
2022", loanId, "1000");
+        return loanId;
+    }
+
+    private Integer 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy()
 {
+
+        String name = Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_", 6);
+        String shortName = Utils.uniqueRandomStringGenerator("", 4);
+
+        List<Integer> principalVariationsForBorrowerCycle = new ArrayList<>();
+        List<Integer> numberOfRepaymentVariationsForBorrowerCycle = new 
ArrayList<>();
+        List<Integer> interestRateVariationsForBorrowerCycle = new 
ArrayList<>();
+        List<ChargeData> charges = new ArrayList<>();
+        List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = new 
ArrayList<>();
+        List<GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings = 
new ArrayList<>();
+
+        String paymentTypeName = PaymentTypeHelper.randomNameGenerator("P_T", 
5);
+        String description = PaymentTypeHelper.randomNameGenerator("PT_Desc", 
15);
+        Boolean isCashPayment = false;
+        Integer position = 1;
+
+        PostPaymentTypesResponse paymentTypesResponse = 
paymentTypeHelper.createPaymentType(new PostPaymentTypesRequest()
+                
.name(paymentTypeName).description(description).isCashPayment(isCashPayment).position(position));
+        Long paymentTypeIdOne = paymentTypesResponse.getResourceId();
+        Assertions.assertNotNull(paymentTypeIdOne);
+
+        List<GetLoanPaymentChannelToFundSourceMappings> 
paymentChannelToFundSourceMappings = new ArrayList<>();
+        GetLoanPaymentChannelToFundSourceMappings 
loanPaymentChannelToFundSourceMappings = new 
GetLoanPaymentChannelToFundSourceMappings();
+        
loanPaymentChannelToFundSourceMappings.fundSourceAccountId(fundReceivables.getAccountID().longValue());
+        
loanPaymentChannelToFundSourceMappings.paymentTypeId(paymentTypeIdOne.longValue());
+        
paymentChannelToFundSourceMappings.add(loanPaymentChannelToFundSourceMappings);
+
+        // fund
+        FundsHelper fh = 
FundsHelper.create(Utils.uniqueRandomStringGenerator("", 
10)).externalId(UUID.randomUUID().toString()).build();
+        String jsonData = fh.toJSON();
+
+        final Long fundID = createFund(jsonData, this.requestSpec, 
this.responseSpec);
+        Assertions.assertNotNull(fundID);
+
+        // Delinquency Bucket
+        final Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
+
+        String futureInstallmentAllocationRule = "NEXT_INSTALLMENT";
+        AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation(futureInstallmentAllocationRule);
+
+        PostLoanProductsRequest loanProductsRequest = new 
PostLoanProductsRequest().name(name)//
+                .shortName(shortName)//
+                .description("Loan Product Description")//
+                .fundId(fundID)//
+                .startDate(null)//
+                .closeDate(null)//
+                .includeInBorrowerCycle(false)//
+                .currencyCode("USD")//
+                .digitsAfterDecimal(2)//
+                .inMultiplesOf(0)//
+                .installmentAmountInMultiplesOf(1)//
+                .useBorrowerCycle(false)//
+                .minPrincipal(100.0)//
+                .principal(1000.0)//
+                .maxPrincipal(10000.0)//
+                .minNumberOfRepayments(1)//
+                .numberOfRepayments(1)//
+                .maxNumberOfRepayments(30)//
+                .isLinkedToFloatingInterestRates(false)//
+                .minInterestRatePerPeriod((double) 0)//
+                .interestRatePerPeriod((double) 0)//
+                .maxInterestRatePerPeriod((double) 0)//
+                .interestRateFrequencyType(2)//
+                .repaymentEvery(30)//
+                .repaymentFrequencyType(0)//
+                
.principalVariationsForBorrowerCycle(principalVariationsForBorrowerCycle)//
+                
.numberOfRepaymentVariationsForBorrowerCycle(numberOfRepaymentVariationsForBorrowerCycle)//
+                
.interestRateVariationsForBorrowerCycle(interestRateVariationsForBorrowerCycle)//
+                .amortizationType(1)//
+                .interestType(0)//
+                .isEqualAmortization(false)//
+                .interestCalculationPeriodType(1)//
+                
.transactionProcessingStrategyCode("advanced-payment-allocation-strategy")//
+                .addPaymentAllocationItem(defaultAllocation)//
+                .daysInYearType(1)//
+                .daysInMonthType(1)//
+                .canDefineInstallmentAmount(true)//
+                .graceOnArrearsAgeing(3)//
+                .overdueDaysForNPA(179)//
+                .accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
+                .principalThresholdForLastInstallment(50)//
+                .allowVariableInstallments(false)//
+                .canUseForTopup(false)//
+                .isInterestRecalculationEnabled(false)//
+                .holdGuaranteeFunds(false)//
+                .multiDisburseLoan(true)//
+                .allowAttributeOverrides(new AllowAttributeOverrides()//
+                        .amortizationType(true)//
+                        .interestType(true)//
+                        .transactionProcessingStrategyCode(true)//
+                        .interestCalculationPeriodType(true)//
+                        .inArrearsTolerance(true)//
+                        .repaymentEvery(true)//
+                        .graceOnPrincipalAndInterestPayment(true)//
+                        .graceOnArrearsAgeing(true))//
+                .allowPartialPeriodInterestCalcualtion(true)//
+                .maxTrancheCount(10)//
+                .outstandingLoanBalance(10000.0)//
+                .charges(charges)//
+                .accountingRule(3)//
+                
.fundSourceAccountId(suspenseClearingAccount.getAccountID().longValue())//
+                
.loanPortfolioAccountId(loansReceivable.getAccountID().longValue())//
+                
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
+                
.interestOnLoanAccountId(interestIncome.getAccountID().longValue())//
+                .incomeFromFeeAccountId(feeIncome.getAccountID().longValue())//
+                
.incomeFromPenaltyAccountId(feeIncome.getAccountID().longValue())//
+                
.incomeFromRecoveryAccountId(recoveries.getAccountID().longValue())//
+                .writeOffAccountId(writtenOff.getAccountID().longValue())//
+                
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
+                
.receivableInterestAccountId(interestFeeReceivable.getAccountID().longValue())//
+                
.receivableFeeAccountId(interestFeeReceivable.getAccountID().longValue())//
+                
.receivablePenaltyAccountId(interestFeeReceivable.getAccountID().longValue())//
+                .dateFormat("dd MMMM yyyy")//
+                .locale("en_GB")//
+                .disallowExpectedDisbursements(true)//
+                .allowApprovedDisbursedAmountsOverApplied(true)//
+                .overAppliedCalculationType("percentage")//
+                .overAppliedNumber(50)//
+                .delinquencyBucketId(delinquencyBucketId.longValue())//
+                
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditInterestAccountId(interestIncomeChargeOff.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditFeesAccountId(feeChargeOff.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditPenaltyAccountId(feeChargeOff.getAccountID().longValue())//
+                
.paymentChannelToFundSourceMappings(paymentChannelToFundSourceMappings)//
+                
.penaltyToIncomeAccountMappings(penaltyToIncomeAccountMappings)//
+                .feeToIncomeAccountMappings(feeToIncomeAccountMappings)//
+                
.incomeFromChargeOffInterestAccountId(interestIncomeChargeOff.getAccountID().longValue())//
+                
.incomeFromChargeOffFeesAccountId(feeChargeOff.getAccountID().longValue())//
+                
.chargeOffExpenseAccountId(creditLossBadDebt.getAccountID().longValue())//
+                
.chargeOffFraudExpenseAccountId(creditLossBadDebtFraud.getAccountID().longValue())//
+                
.incomeFromChargeOffPenaltyAccountId(feeChargeOff.getAccountID().longValue());//
+
+        PostLoanProductsResponse loanProductCreateResponse = 
loanProductHelper.createLoanProduct(loanProductsRequest);
+        return loanProductCreateResponse.getResourceId().intValue();
+    }
+
+    private Long createFund(final String fundJSON, final RequestSpecification 
requestSpec, final ResponseSpecification responseSpec) {
+        String fundId = 
String.valueOf(FundsResourceHandler.createFund(fundJSON, requestSpec, 
responseSpec));
+        if (fundId.equals("null")) {
+            // Invalid JSON data parameters
+            return null;
+        }
+
+        return Long.valueOf(fundId);
+    }
+
+    private AdvancedPaymentData createDefaultPaymentAllocation(String 
futureInstallmentAllocationRule) {
+        AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
+        advancedPaymentData.setTransactionType("DEFAULT");
+        
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
+
+        List<PaymentAllocationOrder> paymentAllocationOrders = 
getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY,
+                PaymentAllocationType.PAST_DUE_FEE, 
PaymentAllocationType.PAST_DUE_PRINCIPAL, 
PaymentAllocationType.PAST_DUE_INTEREST,
+                PaymentAllocationType.DUE_PENALTY, 
PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL,
+                PaymentAllocationType.DUE_INTEREST, 
PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE,
+                PaymentAllocationType.IN_ADVANCE_PRINCIPAL, 
PaymentAllocationType.IN_ADVANCE_INTEREST);
+
+        advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
+        return advancedPaymentData;
+    }
+
+    private List<PaymentAllocationOrder> 
getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
+        AtomicInteger integer = new AtomicInteger(1);
+        return Arrays.stream(paymentAllocationTypes).map(pat -> {
+            PaymentAllocationOrder paymentAllocationOrder = new 
PaymentAllocationOrder();
+            paymentAllocationOrder.setPaymentAllocationRule(pat.name());
+            paymentAllocationOrder.setOrder(integer.getAndIncrement());
+            return paymentAllocationOrder;
+        }).toList();
+    }
+}

Reply via email to