forked from apache/fineract
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FINERACT-1971: Wrong GL entries on refund with reverse replay and cha…
…rge off
- Loading branch information
Showing
3 changed files
with
260 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
215 changes: 215 additions & 0 deletions
215
...ct/integrationtests/LoanChargeOffAccountingEntriesForReverseReplayedTransactionsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
/** | ||
* 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 java.math.BigDecimal; | ||
import java.util.List; | ||
import java.util.UUID; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
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.PostLoansLoanIdTransactionsTransactionIdRequest; | ||
import org.apache.fineract.integrationtests.common.ClientHelper; | ||
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper; | ||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class LoanChargeOffAccountingEntriesForReverseReplayedTransactionsTest extends BaseLoanIntegrationTest { | ||
|
||
@Test | ||
public void testJournalEntriesForChargeOffLoanWithMultipleReverseReplay() { | ||
runAt("24 May 2024", () -> { | ||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); | ||
|
||
// Create DelinquencyBuckets | ||
Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec, List.of(// | ||
Pair.of(1, 10), // | ||
Pair.of(11, 30), // | ||
Pair.of(31, 60), // | ||
Pair.of(61, null)// | ||
)); | ||
|
||
// create loan product | ||
PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct().numberOfRepayments(3)// | ||
.repaymentEvery(15)// | ||
.repaymentFrequencyType(RepaymentFrequencyType.DAYS.longValue())// | ||
.disallowExpectedDisbursements(true)// | ||
.multiDisburseLoan(true)// | ||
.enableDownPayment(true)// | ||
.disbursedAmountPercentageForDownPayment(BigDecimal.valueOf(25.0))// | ||
.enableAutoRepaymentForDownPayment(true); | ||
|
||
loanProductsRequest.setDelinquencyBucketId(delinquencyBucketId.longValue()); | ||
|
||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); | ||
|
||
// Apply and Approve Loan | ||
Long loanId = applyAndApproveLoan(clientId, loanProductResponse.getResourceId(), "24 May 2024", 1000.0, 3); | ||
|
||
// disburse Loan | ||
disburseLoan(loanId, BigDecimal.valueOf(200), "24 May 2024"); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(50.0, "Down Payment", "24 May 2024"), // | ||
transaction(200.0, "Disbursement", "24 May 2024") // | ||
); | ||
|
||
// set business date 25 May | ||
updateBusinessDate("25 May 2024"); | ||
|
||
// reverse downpayment transaction | ||
Long downPaymentTransactionId = getTransactionId(loanId, "Down Payment", "24 May 2024"); | ||
loanTransactionHelper.reverseLoanTransaction(loanId, downPaymentTransactionId, | ||
new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN).transactionDate("25 May 2024") | ||
.transactionAmount(0.0).locale("en")); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(200.0, "Disbursement", "24 May 2024", 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(50.0, "Down Payment", "24 May 2024", 150.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true) // | ||
); | ||
|
||
// set business date 26 May | ||
updateBusinessDate("26 May 2024"); | ||
// charge-off loan | ||
Long chargeOffTransactionId = chargeOffLoan(loanId, "26 May 2024"); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(200.0, "Disbursement", "24 May 2024", 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(50.0, "Down Payment", "24 May 2024", 150.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), // | ||
transaction(200.0, "Charge-off", "26 May 2024", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, false) // | ||
); | ||
|
||
// make backdated repayment on 25 May | ||
Long repaymentTransactionId = addRepaymentForLoan(loanId, 10.0, "25 May 2024"); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(200.0, "Disbursement", "24 May 2024", 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(50.0, "Down Payment", "24 May 2024", 150.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), // | ||
transaction(10.0, "Repayment", "25 May 2024", 190.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(190.0, "Charge-off", "26 May 2024", 0.0, 190.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); | ||
|
||
// make refund on 26 May equal to disbursal amount | ||
String merchantIssuedRefundExternalId = UUID.randomUUID().toString(); | ||
Long merchantIssuedRefundId = loanTransactionHelper.makeMerchantIssuedRefund(loanId, | ||
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("26 May 2024").locale("en") | ||
.transactionAmount(200.0).externalId(merchantIssuedRefundExternalId)) | ||
.getResourceId(); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(200.0, "Disbursement", "24 May 2024", 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(50.0, "Down Payment", "24 May 2024", 150.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), // | ||
transaction(10.0, "Repayment", "25 May 2024", 190.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(190.0, "Charge-off", "26 May 2024", 0.0, 190.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(200.0, "Merchant Issued Refund", "26 May 2024", 0.0, 190.0, 0.0, 0.0, 0.0, 0.0, 10.0, false)); | ||
|
||
// verify loan status is overpaid | ||
verifyLoanStatus(loanId, LoanStatus.OVERPAID); | ||
// verify journal entries | ||
|
||
// repayment | ||
verifyTRJournalEntries(repaymentTransactionId, // | ||
credit(loansReceivableAccount, 10), // | ||
debit(fundSource, 10) // | ||
); | ||
|
||
// charge off | ||
verifyTRJournalEntries(chargeOffTransactionId, // | ||
credit(loansReceivableAccount, 200), // | ||
debit(chargeOffExpenseAccount, 200), // | ||
debit(loansReceivableAccount, 200), // | ||
credit(chargeOffExpenseAccount, 200) // | ||
); | ||
|
||
// refund | ||
verifyTRJournalEntries(merchantIssuedRefundId, // | ||
credit(chargeOffExpenseAccount, 190), // | ||
credit(overpaymentAccount, 10), // | ||
debit(fundSource, 200) | ||
|
||
); | ||
|
||
// set business date 27 May | ||
updateBusinessDate("27 May 2024"); | ||
|
||
// CBR | ||
loanTransactionHelper.makeCreditBalanceRefund(loanId, new PostLoansLoanIdTransactionsRequest().transactionDate("27 May 2024") | ||
.dateFormat(DATETIME_PATTERN).transactionAmount(10.0).locale("en")); | ||
|
||
// verify loan status is closed | ||
verifyLoanStatus(loanId, LoanStatus.CLOSED_OBLIGATIONS_MET); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(200.0, "Disbursement", "24 May 2024", 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(50.0, "Down Payment", "24 May 2024", 150.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), // | ||
transaction(10.0, "Repayment", "25 May 2024", 190.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(190.0, "Charge-off", "26 May 2024", 0.0, 190.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(200.0, "Merchant Issued Refund", "26 May 2024", 0.0, 190.0, 0.0, 0.0, 0.0, 0.0, 10.0, false), // | ||
transaction(10.0, "Credit Balance Refund", "27 May 2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, false)); | ||
|
||
// reverse backdated repayment | ||
loanTransactionHelper.reverseLoanTransaction(loanId, repaymentTransactionId, | ||
new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN).transactionDate("27 May 2024") | ||
.transactionAmount(0.0).locale("en")); | ||
|
||
// verify transactions | ||
verifyTransactions(loanId, // | ||
transaction(200.0, "Disbursement", "24 May 2024", 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(50.0, "Down Payment", "24 May 2024", 150.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), // | ||
transaction(10.0, "Repayment", "25 May 2024", 190.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), // | ||
transaction(200.0, "Charge-off", "26 May 2024", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(200.0, "Merchant Issued Refund", "26 May 2024", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), // | ||
transaction(10.0, "Credit Balance Refund", "27 May 2024", 10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); | ||
|
||
// verify journal entries | ||
|
||
// repayment | ||
verifyTRJournalEntries(repaymentTransactionId, // | ||
credit(loansReceivableAccount, 10), // | ||
debit(fundSource, 10), // | ||
debit(loansReceivableAccount, 10), // | ||
credit(fundSource, 10) // | ||
); | ||
|
||
// replayed merchant issued refund | ||
Long replayedMerchantIssuedRefundId = loanTransactionHelper.getLoanTransactionDetails(loanId, merchantIssuedRefundExternalId) | ||
.getId(); | ||
|
||
verifyTRJournalEntries(replayedMerchantIssuedRefundId, credit(chargeOffExpenseAccount, 200), // | ||
debit(fundSource, 200) // | ||
); | ||
|
||
// verify journal entries for reversed refund | ||
verifyTRJournalEntries(merchantIssuedRefundId, // | ||
credit(chargeOffExpenseAccount, 190), // | ||
credit(overpaymentAccount, 10), // | ||
debit(fundSource, 200), // | ||
debit(chargeOffExpenseAccount, 190), // | ||
debit(overpaymentAccount, 10), // | ||
credit(fundSource, 200)); | ||
|
||
}); | ||
} | ||
} |