-
Notifications
You must be signed in to change notification settings - Fork 2.5k
FINERACT-1289: Taxes on Loan charges #5780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
alberto-art3ch
wants to merge
1
commit into
apache:develop
Choose a base branch
from
openMF:FINERACT-1289/taxes-on-loan-charges
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
128 changes: 128 additions & 0 deletions
128
fineract-doc/src/docs/en/chapters/features/taxes-on-loan-charges.adoc
This file contains hidden or 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,128 @@ | ||
| = Taxes on Loan Charges | ||
|
|
||
| [NOTE] | ||
| ==== | ||
| Introduced in link:https://issues.apache.org/jira/browse/FINERACT-1289[FINERACT-1289] — Tax component not working as expected: tax bifurcation in journal entries was not applied when a tax group was mapped to a loan charge. | ||
| ==== | ||
|
|
||
| == Overview | ||
|
|
||
| When a loan charge (fee or penalty) is linked to a tax group, the system must produce separate journal entry lines for the base charge amount and the tax portion. Prior to this fix, the tax bifurcation was silently skipped and the full charge amount was posted as a single income entry, causing incorrect GL balances and tax liability omissions. | ||
|
|
||
| This fix ensures that every time a loan charge amount is set or recalculated, any configured tax group is evaluated, the tax split is computed per tax component, and both the net charge amount and the tax liability are recorded as distinct journal entries under both accrual-based and cash-based accounting modes. | ||
|
|
||
| === Benefits | ||
|
|
||
| * Tax liability GL accounts are credited correctly when a taxed charge is collected. | ||
| * Income accounts reflect the net-of-tax charge amount rather than the gross amount. | ||
| * Per-component tax breakdowns are persisted, enabling audit trails and reporting. | ||
| * Both accrual and cash accounting modes handle tax bifurcation consistently. | ||
|
|
||
| == Design | ||
|
|
||
| === Key Components | ||
|
|
||
| |=== | ||
| | Component | Module | Purpose | ||
|
|
||
| | `LoanChargeTaxDetails` | ||
| | `fineract-loan` | ||
| | JPA entity (`m_loan_charge_tax_details`) storing the computed tax amount per `TaxComponent` for a single `LoanCharge`. | ||
|
|
||
| | `ChargeTaxApplicationService` / `ChargeTaxApplicationServiceImpl` | ||
| | `fineract-tax` | ||
| | Computes the per-component tax split from a `TaxGroup`, a base amount, and an effective date. | ||
|
|
||
| | `LoanChargeService` | ||
| | `fineract-loan` | ||
| | Calls `applyTaxIfConfigured()` after every charge amount mutation (set, update, recalculate) to keep tax details in sync. | ||
|
|
||
| | `ChargeTaxPaymentDTO` | ||
| | `fineract-provider` | ||
| | Carries per-component tax payment data (charge ID, credit GL account ID, amount, penalty flag) from the loan transaction to the accounting layer. | ||
|
|
||
| | `LoanCommonAccountingHelper` | ||
| | `fineract-provider` | ||
| | Shared helper that filters tax payments by type (fee/penalty), computes net charge amounts, and creates credit/debit journal entries for tax liability accounts. | ||
|
|
||
| | `AccrualBasedAccountingProcessorForLoan` | ||
| | `fineract-provider` | ||
| | Extended to split fee and penalty journal entries into net income and tax liability entries when tax payments are present. | ||
|
|
||
| | `CashBasedAccountingProcessorForLoan` | ||
| | `fineract-provider` | ||
| | Extended with the same tax bifurcation logic for cash-basis accounting. | ||
| |=== | ||
|
|
||
| === Data Model | ||
|
|
||
| Two schema changes are introduced by migration `1035_add_tax_to_loan_charge.xml`. | ||
|
|
||
| [source,sql] | ||
| ---- | ||
| -- Column added to m_loan_charge to store the total tax amount for the charge | ||
| ALTER TABLE m_loan_charge | ||
| ADD COLUMN tax_amount DECIMAL(19,6) NULL; | ||
|
|
||
| -- New table storing the per-component tax breakdown for each loan charge | ||
| CREATE TABLE m_loan_charge_tax_details ( | ||
| id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, | ||
| loan_charge_id BIGINT NOT NULL, -- FK → m_loan_charge.id | ||
| tax_component_id BIGINT NOT NULL, -- FK → m_tax_component.id | ||
| amount DECIMAL(19,6) NOT NULL | ||
| ); | ||
| ---- | ||
|
|
||
| === Accounting Impact | ||
|
|
||
| When a loan transaction pays a charge that has associated tax, the journal entries are split between the net charge amount and the tax liability. | ||
|
|
||
| ==== Accrual-Based Accounting — Fee Payment | ||
|
|
||
| |=== | ||
| | Side | Account | Amount | ||
|
|
||
| | DR | Fees Receivable | Full fee amount (including tax) | ||
| | CR | Income from Fees | Net fee amount (fee minus tax) | ||
| | CR | Tax Liability (per component) | Tax amount | ||
| |=== | ||
|
|
||
| ==== Accrual-Based Accounting — Fee Reversal | ||
|
|
||
| |=== | ||
| | Side | Account | Amount | ||
|
|
||
| | CR | Fees Receivable | Full fee amount (including tax) | ||
| | DR | Income from Fees | Net fee amount | ||
| | DR | Tax Liability (per component) | Tax amount | ||
| |=== | ||
|
|
||
| The same split applies to penalty charges using the penalty income and receivable accounts. Cash-based accounting follows the same debit/credit rules without the receivable leg. | ||
|
|
||
| If no tax group is configured on the charge, or the computed tax is zero, the existing single-entry behaviour is preserved. | ||
|
|
||
| == Configuration | ||
|
|
||
| === Prerequisites | ||
|
|
||
| . Create one or more *Tax Components* (Administration → Tax Configuration → Tax Components) with the applicable percentage rate. | ||
| . Create a *Tax Group* that references the tax components. | ||
| . On the *Charge product*, assign the tax group under the _Tax Group_ field. | ||
| . Add the charge to a *Loan Product* that has either *Periodic Accrual* or *Cash-Based* accounting enabled. | ||
|
|
||
| === Validation Rules | ||
|
|
||
| * Tax is computed at the time the charge amount is set or updated; a later change to the charge amount triggers recomputation. | ||
| * The effective date for tax rate lookup defaults to the charge submission date, falling back to the current business date when the submission date is absent. | ||
| * If the tax group yields a zero total tax (e.g., all components have 0% rate on the effective date), no tax entries are created and the charge behaves as untaxed. | ||
|
|
||
| == Usage Example | ||
|
|
||
| . Configure a tax component "VAT 16%" and a tax group "Standard VAT". | ||
| . Create a flat loan fee of 1,000 and link it to the "Standard VAT" group. | ||
| . Add the fee to a loan product with periodic accrual accounting. | ||
| . After disbursement, the fee appears with `amount = 1,000` and `tax_amount = 160`. | ||
| . When the borrower repays the charge the system posts: | ||
| ** DR Fees Receivable 1,000 | ||
| ** CR Income from Fees 840 | ||
| ** CR Tax Liability 160 |
This file contains hidden or 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 hidden or 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
40 changes: 40 additions & 0 deletions
40
...loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ChargeTaxDetailDTO.java
This file contains hidden or 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,40 @@ | ||
| /** | ||
| * 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.portfolio.loanaccount.data; | ||
|
|
||
| import java.math.BigDecimal; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| /** | ||
| * Carries the pro-rated tax amount for a single TaxComponent when a LoanCharge is (partially) paid. Used to propagate | ||
| * tax details from the domain layer to the accounting bridge. | ||
| */ | ||
| @Data | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class ChargeTaxDetailDTO { | ||
|
|
||
| /** GL account to credit (tax liability account from TaxComponent.creditAccount). */ | ||
| private Long creditAccountId; | ||
|
|
||
| /** Pro-rated tax amount for this component in this payment. */ | ||
| private BigDecimal amount; | ||
| } |
This file contains hidden or 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 hidden or 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
54 changes: 54 additions & 0 deletions
54
.../src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanChargeTaxDetails.java
This file contains hidden or 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,54 @@ | ||
| /** | ||
| * 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.portfolio.loanaccount.domain; | ||
|
|
||
| import jakarta.persistence.Column; | ||
| import jakarta.persistence.Entity; | ||
| import jakarta.persistence.JoinColumn; | ||
| import jakarta.persistence.ManyToOne; | ||
| import jakarta.persistence.Table; | ||
| import java.math.BigDecimal; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
| import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; | ||
| import org.apache.fineract.portfolio.tax.domain.TaxComponent; | ||
|
|
||
| @AllArgsConstructor | ||
| @Setter | ||
| @Getter | ||
| @Entity | ||
| @Table(name = "m_loan_charge_tax_details") | ||
| public class LoanChargeTaxDetails extends AbstractPersistableCustom<Long> { | ||
|
|
||
| @ManyToOne | ||
| @JoinColumn(name = "loan_charge_id", nullable = false) | ||
| private LoanCharge loanCharge; | ||
|
|
||
| @ManyToOne | ||
| @JoinColumn(name = "tax_component_id", nullable = false) | ||
| private TaxComponent taxComponent; | ||
|
|
||
| @Column(name = "amount", scale = 6, precision = 19, nullable = false) | ||
| private BigDecimal amount; | ||
|
|
||
| public LoanChargeTaxDetails() { | ||
|
|
||
| } | ||
| } |
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.