Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@
package org.apache.fineract.test.stepdef.common;

import static org.apache.fineract.client.feign.util.FeignCalls.ok;
import static org.assertj.core.api.Assertions.assertThat;

import io.cucumber.java.en.When;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.feign.FineractFeignClient;
import org.apache.fineract.client.models.PostGlClosuresRequest;
import org.apache.fineract.client.models.PostOfficesRequest;
import org.apache.fineract.client.models.PostOfficesResponse;
import org.apache.fineract.test.helper.Utils;
Expand All @@ -33,6 +37,8 @@
@RequiredArgsConstructor
public class OfficeStepDef extends AbstractStepDef {

private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.ENGLISH);

private final FineractFeignClient fineractClient;

@When("Admin creates a new office")
Expand All @@ -47,4 +53,18 @@ public void createNewOffice() {
final PostOfficesResponse response = ok(() -> fineractClient.offices().createOffice(request));
testContext().set(TestContextKey.OFFICE_CREATE_RESPONSE, response);
}

@When("Admin closes accounting for the last created office on {string}")
public void closeAccountingForLastCreatedOffice(final String closingDate) {
final PostOfficesResponse office = testContext().get(TestContextKey.OFFICE_CREATE_RESPONSE);
assertThat(office).as("No office was created. Use 'Admin creates a new office' step first.").isNotNull();
final PostGlClosuresRequest request = new PostGlClosuresRequest()//
.officeId(office.getOfficeId())//
.closingDate(LocalDate.parse(closingDate, FORMATTER))//
.comments("Accounting closure")//
.dateFormat("yyyy-MM-dd")//
.locale("en");//

ok(() -> fineractClient.accountingClosure().createGLClosure(request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,57 @@ Feature: Working Capital Discount Adjustment Backdated and Undo
| LIABILITY | 240005 | Deferred Interest Revenue | 28.7 | |
| INCOME | 404000 | Interest Income | | 28.7 |

Scenario: Full discount adjustment on a product without accounting posts an amortization adjustment with no journal entries; undo reverses both even when accounting is closed for the office
When Admin sets the business date to "01 January 2026"
And Admin creates a new office
And Admin creates a client with random data in the last created office
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | |
Then Working capital loan creation was successful
Then Admin successfully approves the working capital loan on "01 January 2026" with "9000" amount and expected disbursement date on "01 January 2026"
Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "9000" EUR transaction amount
Then Admin successfully add discount with "1000" amount on Working Capital loan account
And Admin loads discount fee transaction from Working Capital loan for adjustment
When Admin sets the business date to "02 January 2026"
And Customer makes repayment on "02 January 2026" with 50 transaction amount on Working Capital loan
When Admin sets the business date to "03 January 2026"
And Customer makes repayment on "03 January 2026" with 50 transaction amount on Working Capital loan
When Admin sets the business date to "04 January 2026"
And Customer makes repayment on "04 January 2026" with 50 transaction amount on Working Capital loan
When Admin sets the business date to "05 January 2026"
When Admin runs inline COB job for Working Capital Loan
And Admin adds Discount fee adjustment with "1000" amount on transaction date "05 January 2026" on Working Capital loan account for last discount
When Admin sets the business date to "06 January 2026"
When Admin runs inline COB job for Working Capital Loan
Then Working Capital Loan has transactions:
| transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed |
| 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false |
| 01 January 2026 | Discount Fee | 1000.0 | 1000.0 | 0.0 | 0.0 | false |
| 02 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false |
| 03 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false |
| 04 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false |
| 04 January 2026 | Discount Fee Amortization | 28.70 | | | | false |
| 05 January 2026 | Discount Fee Adjustment | 1000.0 | 1000.0 | 0.0 | 0.0 | false |
| 05 January 2026 | Discount Fee Amortization Adjustment | 28.70 | | | | false |
Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE_AMORTIZATION" transaction with date "04 January 2026" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE_AMORTIZATION_ADJUSTMENT" transaction with date "05 January 2026" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
When Admin closes accounting for the last created office on "06 January 2026"
When Admin undo the last Discount fee adjustment on Working Capital loan account
Then Working Capital Loan has transactions:
| transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed |
| 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false |
| 01 January 2026 | Discount Fee | 1000.0 | 1000.0 | 0.0 | 0.0 | false |
| 02 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false |
| 03 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false |
| 04 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false |
| 04 January 2026 | Discount Fee Amortization | 28.70 | | | | false |
| 05 January 2026 | Discount Fee Adjustment | 1000.0 | 1000.0 | 0.0 | 0.0 | true |
| 05 January 2026 | Discount Fee Amortization Adjustment | 28.70 | | | | true |
Then Admin closes the Working Capital loan with a full repayment on "06 January 2026"

@TestRailId:C83068
Scenario: Multiple backdated discount fee adjustments are allowed; undo of the last one restores its share of the discount
When Admin sets the business date to "01 January 2026"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,30 @@ Feature: Working Capital Loan Charge Accrual
| 50.0 | 50.0 | 0.0 | 30.0 | 30.0 | 0.0 |
And Admin sets the business date to "20 October 2026"
Then Admin closes the Working Capital loan with a full repayment on "20 October 2026"
And Working Capital Loan has transactions:
| transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed |
| 01 October 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false |
| 20 October 2026 | Repayment | 9080.0 | 9000.0 | 50.0 | 30.0 | false |

Scenario: Verify no accrual or accrual adjustment is created for Working Capital loan charges in real time and on undo disbursal when product accounting rule is NONE
Given Admin sets the business date to "01 May 2027"
And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount |
| WCLP | 01 May 2027 | 01 May 2027 | 9000 | 100000 | 18 | 0 |
When Global config "charge-accrual-date" value set to "submitted-date"
When Global config "wcl-charge-accrual-time" value set to "real-time"
And Admin sets the business date to "10 May 2027"
And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "20 May 2027" due date and 100.0 transaction amount
Then Working Capital Loan has transactions:
| transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed |
| 01 May 2027 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false |
When Admin successfully undo Working Capital disbursal
Then Working Capital Loan has transactions:
| transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed |
| 01 May 2027 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | true |
And Admin successfully disburse the Working Capital loan on "10 May 2027" with "9000" EUR transaction amount
And Admin sets the business date to "15 May 2027"
Then Admin closes the Working Capital loan with a full repayment on "15 May 2027"

@TestRailId:C85643
Scenario: Verify pending charge accrual is posted at closure even when charge-accrual-date is submitted-date
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,25 @@ Feature: Working Capital Charge-Off Accounting Entries
| Type | Account code | Account name | Debit | Credit |
Then Admin closes the Working Capital loan with all obligations met with a full repayment on "10 January 2026"

Scenario: Verify Working Capital charge-off on a product without accounting posts the final discount fee amortization with no journal entries
Given Admin sets the business date to "01 January 2026"
And Admin creates a client with random data and creates-approves-disburses a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount |
| WCLP | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 |
And Admin adds Discount fee with "1000" amount on Working Capital loan account for last disbursement
When Admin sets the business date to "02 January 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
And Admin charges off the Working Capital loan on "02 January 2026"
Then Working Capital Loan has transactions:
| transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed |
| 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false |
| 01 January 2026 | Discount Fee | 1000.0 | 1000.0 | 0.0 | 0.0 | false |
| 02 January 2026 | Charge-off | 10000.0 | 10000.0 | 0.0 | 0.0 | false |
| 02 January 2026 | Discount Fee Amortization | 1000.0 | | | | false |
Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE_AMORTIZATION" transaction with date "02 January 2026" which has the following Journal entries:
| Type | Account code | Account name | Debit | Credit |
Then Admin closes the Working Capital loan with all obligations met with a full repayment on "02 January 2026"

@TestRailId:C94033
Scenario: Verify Working Capital charge-off accounting - UC24: backdated repayment before charge-off keeps regular JE and restates charge-off
Given Admin sets the business date to "01 January 2026"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1309,7 +1309,9 @@ private WorkingCapitalLoanTransaction reverseDisbursementTransactionAndResetBala

// Reverse the journal entries of any charge accrual so the recognized income/receivable is backed out with the
// disbursement; marking the transaction reversed alone would leave the GL postings in place.
accrualsToReverse.forEach(accrual -> accountingProcessor.postReversalJournalEntries(loan, accrual));
if (loan.getLoanProduct().getAccountingRule().isAccrualWithDeferredRevenueAmortization()) {
accrualsToReverse.forEach(accrual -> accountingProcessor.postReversalJournalEntries(loan, accrual));
}
accrualsToReverse.forEach(accrual -> businessEventNotifierService
.notifyPostBusinessEvent(new WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent(accrual, loan.getId())));

Expand Down Expand Up @@ -1365,7 +1367,9 @@ private void reverseDiscountFeeAmortizationAdjustments(final WorkingCapitalLoan
LoanTransactionType.DISCOUNT_FEE_AMORTIZATION_ADJUSTMENT).forEach(relation -> {
final WorkingCapitalLoanTransaction txn = relation.getFromTransaction();
reverseTransaction(txn);
accountingProcessor.postReversalJournalEntries(loan, txn);
if (loan.getLoanProduct().getAccountingRule().isAccrualWithDeferredRevenueAmortization()) {
accountingProcessor.postReversalJournalEntries(loan, txn);
}
});
// Realized income is recomputed from the (now-reversed) transactions by its single owner, not adjusted here.
discountFeeAmortizationService.recalculateRealizedIncome(loan);
Expand Down
Loading