From 857657a61c68e2f5db21afc8cdbfc14030281a5f Mon Sep 17 00:00:00 2001 From: Oleksii Novikov Date: Mon, 8 Jun 2026 18:11:37 +0300 Subject: [PATCH] FINERACT-2455: WC - accounting entries for discount fee and adjustment --- .../WorkingCapitalDiscountAdjustment.feature | 36 +++++++++++++++++++ ...WorkingCapitalDiscountAmortization.feature | 4 +++ ...ountingProcessorForWorkingCapitalLoan.java | 35 ++++++++++++++++++ ...WorkingCapitalLoanAccountingProcessor.java | 4 +++ ...ngCapitalLoanWritePlatformServiceImpl.java | 8 +++++ 5 files changed, 87 insertions(+) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAdjustment.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAdjustment.feature index 39e85d0609f..345bb63acd7 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAdjustment.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAdjustment.feature @@ -282,6 +282,10 @@ Feature: Working Capital Discount Adjustment | 01 January 2026 | Disbursement | 100.0 | 100.0 | 0.0 | 0.0 | false | | 01 January 2026 | Discount Fee | 12.0 | 12.0 | 0.0 | 0.0 | false | | 02 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false | + Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE" transaction with date "01 January 2026" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | 12.0 | | + | LIABILITY | 240005 | Deferred Interest Revenue | | 12.0 | And Admin adds Discount fee adjustment with "10" amount on transaction date "02 January 2026" on Working Capital loan account for last discount And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | @@ -289,9 +293,41 @@ Feature: Working Capital Discount Adjustment | 01 January 2026 | Discount Fee | 12.0 | 12.0 | 0.0 | 0.0 | false | | 02 January 2026 | Repayment | 50.0 | 50.0 | 0.0 | 0.0 | false | | 02 January 2026 | Discount Fee Adjustment | 10.0 | 10.0 | 0.0 | 0.0 | false | + Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE_ADJUSTMENT" transaction with date "02 January 2026" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | LIABILITY | 240005 | Deferred Interest Revenue | 10.0 | | + | ASSET | 112601 | Loans Receivable | | 10.0 | And WorkingCapitalLoanDiscountFeeTransactionBusinessEvent is raised with amount "12" on "01 January 2026" date And WorkingCapitalLoanDiscountFeeAdjustmentTransactionBusinessEvent is raised with amount "10" on "02 January 2026" date + Scenario: Verify Discount fee and multiple Discount fee adjustments on the same day post correct journal entries + When Admin sets the business date to "01 January 2026" + And Admin creates a client with random data + And Admin creates a working capital loan with the following data: + | LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount | + | WCLP_ACCOUNTING_CASH_BASED | 01 January 2026 | 01 January 2026 | 100 | 100 | 18 | 0 | + And Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026" + And Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount + Then Admin adds Discount fee with "12" amount on Working Capital loan account for last disbursement + Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE" transaction with date "01 January 2026" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | 12.0 | | + | LIABILITY | 240005 | Deferred Interest Revenue | | 12.0 | + And Admin adds Discount fee adjustment with "5" amount on Working Capital loan account for last discount + And Admin adds Discount fee adjustment with "7" amount on Working Capital loan account for last discount + And Working Capital Loan has transactions: + | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | + | 01 January 2026 | Disbursement | 100.0 | 100.0 | 0.0 | 0.0 | false | + | 01 January 2026 | Discount Fee | 12.0 | 12.0 | 0.0 | 0.0 | false | + | 01 January 2026 | Discount Fee Adjustment | 5.0 | 5.0 | 0.0 | 0.0 | false | + | 01 January 2026 | Discount Fee Adjustment | 7.0 | 7.0 | 0.0 | 0.0 | false | + Then Working Capital Loan Transactions tab has 2 "DISCOUNT_FEE_ADJUSTMENT" transactions with date "01 January 2026" which have the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | LIABILITY | 240005 | Deferred Interest Revenue | 5.0 | | + | ASSET | 112601 | Loans Receivable | | 5.0 | + | LIABILITY | 240005 | Deferred Interest Revenue | 7.0 | | + | ASSET | 112601 | Loans Receivable | | 7.0 | + @TestRailId:C83036 Scenario: Verify discount fee adjustment transaction with classification field set - UC13 When Admin sets the business date to "01 January 2026" diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAmortization.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAmortization.feature index 95a965ddefd..82d7c47a9cf 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAmortization.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDiscountAmortization.feature @@ -57,6 +57,10 @@ Feature: WorkingCapitalDiscountFeeAmortization | Type | Account code | Account name | Debit | Credit | | INCOME | 404000 | Interest Income | | 28.7 | | LIABILITY | 240005 | Deferred Interest Revenue | 28.7 | | + Then Working Capital Loan Transactions tab has a "DISCOUNT_FEE" transaction with date "01 January 2026" which has the following Journal entries: + | Type | Account code | Account name | Debit | Credit | + | ASSET | 112601 | Loans Receivable | 1000.0 | | + | LIABILITY | 240005 | Deferred Interest Revenue | | 1000.0 | @TestRailId:C80969 Scenario: Verify NO Discount Fee Amortization transaction on Working Capital Loan account triggers on COB without repayment - UC2 diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForWorkingCapitalLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForWorkingCapitalLoan.java index 29d82859090..4ea16c47464 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForWorkingCapitalLoan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForWorkingCapitalLoan.java @@ -253,6 +253,41 @@ public void postJournalEntriesForDiscountFeeAmortizationAdjustment(final Working } } + @Override + public void postJournalEntriesForDiscountFee(final WorkingCapitalLoan loan, final WorkingCapitalLoanTransaction txn) { + postDiscountFeeDeferralEntries(loan, txn, CashAccountsForLoan.LOAN_PORTFOLIO, CashAccountsForLoan.DEFERRED_INCOME_LIABILITY); + } + + @Override + public void postJournalEntriesForDiscountFeeAdjustment(final WorkingCapitalLoan loan, final WorkingCapitalLoanTransaction txn) { + postDiscountFeeDeferralEntries(loan, txn, CashAccountsForLoan.DEFERRED_INCOME_LIABILITY, CashAccountsForLoan.LOAN_PORTFOLIO); + } + + private void postDiscountFeeDeferralEntries(final WorkingCapitalLoan loan, final WorkingCapitalLoanTransaction txn, + final CashAccountsForLoan debitAccountType, final CashAccountsForLoan creditAccountType) { + final Office office = loan.getClient().getOffice(); + final Long productId = loan.getLoanProduct().getId(); + final String currencyCode = loan.getLoanProductRelatedDetails().getCurrency().getCode(); + final LocalDate transactionDate = txn.getTransactionDate(); + final Long loanId = loan.getId(); + final Long txnId = txn.getId(); + final BigDecimal amount = txn.getTransactionAmount(); + + helper.checkForBranchClosures(helper.getLatestClosureByBranch(office.getId()), transactionDate); + + if (MathUtil.isGreaterThanZero(amount)) { + final GLAccount debitAccount = helper.getLinkedGLAccountForWorkingCapitalLoanProduct(productId, debitAccountType.getValue(), + null); + helper.createDebitJournalEntryForWorkingCapitalLoan(office, currencyCode, debitAccount, loanId, txnId, transactionDate, amount, + null); + + final GLAccount creditAccount = helper.getLinkedGLAccountForWorkingCapitalLoanProduct(productId, creditAccountType.getValue(), + null); + helper.createCreditJournalEntryForWorkingCapitalLoan(office, currencyCode, creditAccount, loanId, txnId, transactionDate, + amount, null); + } + } + private Long extractPaymentTypeId(final WorkingCapitalLoanTransaction txn) { if (txn.getPaymentDetail() != null && txn.getPaymentDetail().getPaymentType() != null) { return txn.getPaymentDetail().getPaymentType().getId(); diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/accounting/WorkingCapitalLoanAccountingProcessor.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/accounting/WorkingCapitalLoanAccountingProcessor.java index 4d7c0e00dbe..1a9dcefa18b 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/accounting/WorkingCapitalLoanAccountingProcessor.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/accounting/WorkingCapitalLoanAccountingProcessor.java @@ -33,4 +33,8 @@ void postJournalEntries(WorkingCapitalLoan loan, WorkingCapitalLoanTransaction t void postJournalEntriesForDiscountFeeAmortizationAdjustment(WorkingCapitalLoan loan, WorkingCapitalLoanTransaction txn, boolean isChargedOff); + + void postJournalEntriesForDiscountFee(WorkingCapitalLoan loan, WorkingCapitalLoanTransaction txn); + + void postJournalEntriesForDiscountFeeAdjustment(WorkingCapitalLoan loan, WorkingCapitalLoanTransaction txn); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java index 027611ff226..0145f25c3c9 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java @@ -431,6 +431,10 @@ private WorkingCapitalLoanTransaction createAndPersistDiscountFeeTransaction(fin amortizationScheduleWriteService.generateAndSaveAmortizationScheduleOnDisbursement(loan, disbursementTransaction.getTransactionAmount(), disbursementTransaction.getTransactionDate()); + if (loan.getLoanProduct().getAccountingRule().isCashBased()) { + accountingProcessor.postJournalEntriesForDiscountFee(loan, discountTransaction); + } + businessEventNotifierService .notifyPostBusinessEvent(new WorkingCapitalLoanDiscountFeeTransactionBusinessEvent(discountTransaction)); return discountTransaction; @@ -556,6 +560,10 @@ public CommandProcessingResult makeDiscountFeeAdjustment(final Long loanId, fina saveNewTransactionRelation(adjustmentTransaction, relatedDiscountTransaction, LoanTransactionRelationTypeEnum.RELATED); allocationRepository.saveAndFlush(WorkingCapitalLoanTransactionAllocation.forDiscountFeeAdjustment(adjustmentTransaction, amount)); + if (loan.getLoanProduct().getAccountingRule().isCashBased()) { + accountingProcessor.postJournalEntriesForDiscountFeeAdjustment(loan, adjustmentTransaction); + } + if (loan.getLoanProductRelatedDetails() == null) { throw new PlatformApiDataValidationException("validation.msg.wc.loan.discount.not.available", "Discount adjustment is not available when loan product details are missing", "loanProductRelatedDetails");