FINERACT-2232: Add capitalize income documentation
This commit is contained in:
committed by
Adam Saghy
parent
9ed26cb236
commit
68474e250d
@@ -0,0 +1,490 @@
|
||||
= Capitalized Income
|
||||
|
||||
== Overview
|
||||
|
||||
Capitalized Income (formerly referred to as Origination Fee) in Apache Fineract is a fee-based or interest-based income item that is added to the principal of a loan and amortized over time. It is designed to be applied at disbursement and is treated as part of the loan's principal for repayment and interest calculations.
|
||||
|
||||
== Purpose
|
||||
|
||||
This functionality enables financial institutions to:
|
||||
|
||||
* Recognize deferred income (fees or interest) systematically over the loan term
|
||||
* Align accounting practices with regulatory requirements
|
||||
* Improve income recognition accuracy
|
||||
|
||||
== Supported Loan Type
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
Capitalized Income is only supported for:
|
||||
|
||||
* Progressive Loan Schedules
|
||||
* Advanced Payment Allocation Strategy
|
||||
|
||||
Other loan schedule types and transaction processing strategies are not supported.
|
||||
====
|
||||
|
||||
== Configuration at Loan Product Level
|
||||
|
||||
Capitalized income must be configured on the loan product.
|
||||
|
||||
The configuration options include:
|
||||
|
||||
* *Enable Capitalized Income*: Boolean toggle (default: disabled)
|
||||
* *Calculation Mode*: Only "Flat" is currently supported
|
||||
** Later "Percentage based" can be introduced
|
||||
* *Amortization Strategy*: Only "EQUAL_AMORTIZATION" is supported
|
||||
*** Daily equal portions are recognized over the life of the loan
|
||||
*** Later other strategies can be introduced
|
||||
* *Income Type*: Specifies allocation rule. Defines which "balance category" to be used.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
In Fineract, balance of a transaction is either: Principal, Fee, Penalty, Interest or overpayment
|
||||
====
|
||||
|
||||
Options:
|
||||
* FEE (default)
|
||||
* INTEREST
|
||||
|
||||
=== GL Mapping
|
||||
|
||||
Required GL Account mappings when Capitalized Income is enabled:
|
||||
|
||||
* *Deferred Income (Liability)*: `deferredIncomeLiabilityAccountId` - mandatory when enabled
|
||||
* *Income from Capitalization (Income)*: `incomeFromCapitalizationAccountId` - mandatory when enabled
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
Both GL accounts become mandatory when `enableIncomeCapitalization` is set to `true`.
|
||||
====
|
||||
|
||||
=== Configuration Dependencies
|
||||
|
||||
When `enableIncomeCapitalization` is set to `true`, all following parameters become mandatory:
|
||||
|
||||
* `capitalizedIncomeCalculationType` - must be "FLAT"
|
||||
* `capitalizedIncomeStrategy` - must be "EQUAL_AMORTIZATION"
|
||||
* `capitalizedIncomeType` - must be "FEE" or "INTEREST"
|
||||
* `deferredIncomeLiabilityAccountId` - must reference a valid GL account
|
||||
* `incomeFromCapitalizationAccountId` - must reference a valid GL account
|
||||
|
||||
== Behavior and Calculations
|
||||
|
||||
* Capitalized income is added via API on or after the first disbursement date
|
||||
* It is treated as a principal portion, recalculating the repayment schedule accordingly
|
||||
* Interest and amortization schedules are updated to include the capitalized income amount
|
||||
* Validated using formula: `(Total Disbursed + Current Capitalized Income + New Transaction Amount) ≤ Max Amount`, where Max Amount depends on loan product configuration: if `allowApprovedDisbursedAmountsOverApplied = true` uses `getOverAppliedMax(loan)`, otherwise uses `getApprovedPrincipal()`
|
||||
|
||||
=== Daily Amortization
|
||||
|
||||
* Recognized daily using the configured strategy
|
||||
* Recognized portions move from Deferred Income to Income from Capitalization
|
||||
|
||||
==== Special Handling
|
||||
|
||||
* *Preclosure*: Remaining balance recognized in full on the preclosure date
|
||||
* *Charge-off*: Amortization stops and remaining balance is charged off
|
||||
|
||||
== Transaction Types Introduced
|
||||
|
||||
* Capitalized Income
|
||||
* Capitalized Income Amortization
|
||||
* Capitalized Income Adjustment
|
||||
* Capitalized Income Amortization Adjustment
|
||||
|
||||
=== Capitalized Income Transaction
|
||||
|
||||
The Capitalized Income transaction in Apache Fineract performs the following actions:
|
||||
|
||||
* Adds a specified amount to the loan principal
|
||||
** Considered a deferred income item (such as a fee or interest)
|
||||
** Booked as part of the loan's principal
|
||||
** Added post-disbursement and only if the loan type supports it (currently: Progressive Loans)
|
||||
* Creates a distinct loan transaction
|
||||
** Separately tracked with its own transaction type ("Capitalized Income")
|
||||
** Not merged with disbursements or repayments
|
||||
* Updates the loan schedule
|
||||
** Recalculates amortization and interest schedule to include the added amount in the outstanding principal
|
||||
* Triggers accounting entries
|
||||
** Debits "Loan Portfolio" (Asset)
|
||||
** Credits "Deferred Income" (Liability)
|
||||
** Does not recognize income upfront
|
||||
* Initiates daily amortization
|
||||
** Source for daily income recognition through "Capitalized Income Amortization" transactions
|
||||
** Progressively converts the deferred amount to recognized income
|
||||
|
||||
==== Accounting Entries
|
||||
|
||||
[cols="2*"]
|
||||
|===
|
||||
|Scenario |Debit |Credit
|
||||
|
||||
|Capitalized Income
|
||||
|Loan Portfolio (Asset)
|
||||
|Deferred Income (Liability)
|
||||
|===
|
||||
|
||||
=== Capitalized Income Amortization
|
||||
|
||||
A Capitalized Income Amortization transaction in Apache Fineract does the following:
|
||||
|
||||
* *Recognizes Deferred Income Over Time*: Transfers a portion of the capitalized income (originally posted as a liability) into recognized income (posted as interest or fee income), based on a configured daily amortization strategy.
|
||||
|
||||
* *Daily Posting*: The system automatically creates this transaction each day from the date of capitalized income until the loan maturity or until the full amount is amortized. This is handled by a background job during the COB (Close of Business) process.
|
||||
|
||||
* *Uses Equal Amortization*: The default and only supported strategy is Equal Amortization, which divides the total capitalized income evenly over the remaining number of days until the loan matures.
|
||||
|
||||
==== Accounting Entries
|
||||
|
||||
[cols="2*"]
|
||||
|===
|
||||
|Scenario |Debit |Credit
|
||||
|
||||
|Daily amortization
|
||||
|Deferred Income (Liability)
|
||||
|Income from Capitalization (Income)
|
||||
|===
|
||||
|
||||
==== Stops on Events
|
||||
|
||||
* *Preclosure*: Triggers final amortization for remaining unrecognized income
|
||||
* *Charge-off*: Halts further amortization; the remaining deferred income is charged off
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Reversal Handling: If the original Capitalized Income transaction is reversed, all associated amortization transactions are also reversed via "Capitalized Income Amortization Adjustment" transactions.
|
||||
====
|
||||
|
||||
=== Capitalized Income Adjustment
|
||||
|
||||
A Capitalized Income Adjustment transaction in Apache Fineract serves to reduce the balance of an existing capitalized income transaction.
|
||||
|
||||
==== Purpose
|
||||
|
||||
* Correct overcharged or misposted capitalized income amounts
|
||||
* Reflect fee waivers or negotiated reductions
|
||||
* Support backdated corrections if needed
|
||||
|
||||
==== Transaction Behavior
|
||||
|
||||
* It is a credit-type transaction, reducing the capitalized income balance
|
||||
* Treated similarly to other credit transactions and follows a defined allocation strategy
|
||||
* Can be backdated, but not dated before the original capitalized income transaction
|
||||
|
||||
==== Validation Rules
|
||||
|
||||
* The adjustment amount must not exceed the remaining amount (original capitalized income amount minus total previous adjustments)
|
||||
* Adjustment is linked to a specific Capitalized Income transaction (by ID)
|
||||
* Multiple adjustments can be made against the same original transaction
|
||||
* Adjustments can be reversed if needed
|
||||
|
||||
==== Accounting Entries
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Scenario |Debit |Credit
|
||||
|
||||
|Adjustment ≤ unrecognized balance
|
||||
|Deferred Income (Liability)
|
||||
|Loan Portfolio (Asset)
|
||||
|
||||
|Adjustment > unrecognized balance
|
||||
|Deferred Income (Liability)
|
||||
|Loan Portfolio (Asset)
|
||||
|===
|
||||
|
||||
==== Business Event Triggers
|
||||
|
||||
* Triggers "Capitalized Income Adjustment" event
|
||||
* Updates loan balance and possibly loan status depending on impact
|
||||
|
||||
==== Impact
|
||||
|
||||
* Reduces amortization basis
|
||||
* May modify future amortization amounts
|
||||
* Repayment schedule is not affected directly unless recalculated manually
|
||||
|
||||
=== Capitalized Income Amortization Adjustment
|
||||
|
||||
A Capitalized Income Amortization Adjustment in Apache Fineract is a special transaction type used to reverse previously recognized income from capitalized income amortization.
|
||||
|
||||
==== Purpose
|
||||
|
||||
* Automatically generated when a Capitalized Income transaction is reversed or when backdated Capitalized Income Adjustment affects amortization balances
|
||||
* Reverses all already recognized portions (amortized income) linked to the original Capitalized Income transaction
|
||||
|
||||
==== When It Occurs
|
||||
|
||||
* Created by daily amortization (COB) or final amortization (triggered on loan closure, charge-off, or by any backdated transaction that affects capitalized income balances)
|
||||
* Reverses previously recognized income when amortization needs to be adjusted
|
||||
* Restores Deferred Income balances and reverses income recognition
|
||||
|
||||
==== Accounting Entries
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Transaction Type |Debit |Credit
|
||||
|
||||
|Capitalized Income Amortization Adjustment
|
||||
|Income from Capitalization (Income)
|
||||
|Deferred Income (Liability)
|
||||
|===
|
||||
|
||||
==== Key Characteristics
|
||||
|
||||
* *System-Generated Only*: Cannot be created manually by API or UI
|
||||
* *Ensures Accounting Integrity*: Keeps amortized and unrecognized balances aligned after reversals
|
||||
* *Non-monetary transaction - does not trigger balance changed or status update events*
|
||||
|
||||
==== Business Events
|
||||
|
||||
* Triggers a new business event: Capitalized Income Amortization Adjustment
|
||||
|
||||
== API Endpoints
|
||||
|
||||
=== Configure Capitalized Income on Loan Product
|
||||
|
||||
* *Endpoint*: `/loanproducts`
|
||||
* *Method*: `POST`
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
...
|
||||
"enableIncomeCapitalization": true, // Mandatory
|
||||
"capitalizedIncomeCalculationType": "FLAT", // Mandatory when enabled
|
||||
"capitalizedIncomeStrategy": "EQUAL_AMORTIZATION", // Mandatory when enabled
|
||||
"capitalizedIncomeType": "FEE", // Mandatory when enabled
|
||||
"deferredIncomeLiabilityAccountId": 123, // Mandatory when enabled
|
||||
"incomeFromCapitalizationAccountId": 456 // Mandatory when enabled
|
||||
}
|
||||
----
|
||||
|
||||
=== Add Capitalized Income
|
||||
|
||||
* *Endpoint*: `/loans/{loanId}/transactions?command=capitalizedIncome`
|
||||
* *Alternative Endpoint*: `/loans/external-id/{loanExternalId}/transactions?command=capitalizedIncome`
|
||||
* *Method*: `POST`
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"transactionDate": "2025-05-01", // Mandatory
|
||||
"dateFormat": "yyyy-MM-dd", // Mandatory
|
||||
"locale": "en", // Mandatory
|
||||
"transactionAmount": 100.0, // Mandatory
|
||||
"paymentTypeId": 1, // Optional
|
||||
"note": "Capitalized income fee", // Optional
|
||||
"externalId": "CINCOME-001" // Optional
|
||||
}
|
||||
----
|
||||
|
||||
=== Get Capitalized Income Amortization Info
|
||||
|
||||
* *Endpoint*: `/loans/{loanId}/deferredincome`
|
||||
* *Alternative Endpoint*: `/loans/external-id/{loanExternalId}/deferredincome`
|
||||
* *Method*: `GET`
|
||||
|
||||
==== Response Body
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"capitalizedIncomeData": [
|
||||
{
|
||||
"amount": 50.0, // Total capitalized income amount
|
||||
"amortizedAmount": 1.1, // Amount already amortized
|
||||
"unrecognizedAmount": 48.9, // Amount not yet amortized
|
||||
"amountAdjustment": 0.0, // Any adjustments made
|
||||
"chargedOffAmount": 0.0 // Amount charged off (if applicable)
|
||||
}
|
||||
]
|
||||
}
|
||||
----
|
||||
|
||||
=== Add Capitalized Income Adjustment
|
||||
|
||||
* *Endpoint*: `/loans/{loanId}/transactions/{capitalizedIncomeTransactionId}?command=capitalizedIncomeAdjustment`
|
||||
* *Alternative Endpoint*: `/loans/external-id/{loanExternalId}/transactions/{capitalizedIncomeTransactionId}?command=capitalizedIncomeAdjustment`
|
||||
* *Method*: `POST`
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"transactionDate": "2025-05-01", // Mandatory
|
||||
"dateFormat": "yyyy-MM-dd", // Mandatory
|
||||
"locale": "en", // Mandatory
|
||||
"transactionAmount": 50.0, // Mandatory
|
||||
"paymentTypeId": 1, // Optional
|
||||
"note": "Capitalized income fee", // Optional
|
||||
"externalId": "CINCOMEADJ-001" // Optional
|
||||
}
|
||||
----
|
||||
|
||||
==== Response Body
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"resourceId": 1,
|
||||
"resourceExternalId": "CINCOMEADJ-001"
|
||||
}
|
||||
----
|
||||
|
||||
=== Capitalized Income Template API (to retrieve limits)
|
||||
|
||||
* *Endpoint*: `/loans/{loanId}/transactions/template?command=capitalizedIncome`
|
||||
* *Alternative Endpoint*: `/loans/external-id/{loanExternalId}/transactions/template?command=capitalizedIncome`
|
||||
* *Method*: `GET`
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"paymentTypeOptions": [], // List of available payment types
|
||||
"currency": {...}, // Currency configuration
|
||||
"date": [2025, 5, 29], // Return the current date
|
||||
"amount": 0 // Return the maximum amount that can be capitalized (approved amount - disbursed amount - capitalized income)
|
||||
}
|
||||
----
|
||||
|
||||
=== Capitalized Income Adjustment Template API (to retrieve limits)
|
||||
|
||||
* *Endpoint*: `/loans/{loanId}/transactions/template?command=capitalizedIncomeAdjustment&transactionId={capitalizedIncomeTransactionId}`
|
||||
* *Alternative Endpoint*: `/loans/external-id/{loanExternalId}/transactions/template?command=capitalizedIncomeAdjustment&transactionId={capitalizedIncomeTransactionId}`
|
||||
* *Method*: `GET`
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"paymentTypeOptions": [], // List of available payment types
|
||||
"currency": {...}, // Currency configuration
|
||||
"date": [2025, 5, 29], // Return the current date
|
||||
"amount": 0 // Return the maximum amount that can be adjusted (capitalized income - adjustment)
|
||||
}
|
||||
----
|
||||
|
||||
== Accounting Entries
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Transaction Type |Debit |Credit
|
||||
|
||||
|Capitalized Income
|
||||
|Loan Portfolio (Asset)
|
||||
|Deferred Income (Liability)
|
||||
|
||||
|Capitalized Income Amortization
|
||||
|Deferred Income (Liability)
|
||||
|Income from Capitalization (Income)
|
||||
|
||||
|Capitalized Income Adjustment
|
||||
|Deferred Income (Liability)
|
||||
|Loan Portfolio (Asset)
|
||||
|
||||
|Capitalized Income Amortization Adjustment
|
||||
|Income from Capitalization (Income)
|
||||
|Deferred Income (Liability)
|
||||
|===
|
||||
|
||||
== Business Events
|
||||
|
||||
=== Triggered for Capitalized Income
|
||||
|
||||
* `LoanCapitalizedIncomeTransactionCreatedBusinessEvent`
|
||||
* `LoanBalanceChangedBusinessEvent`
|
||||
|
||||
=== Daily Amortization
|
||||
|
||||
* `LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent`
|
||||
* `LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent`
|
||||
|
||||
=== Capitalized Income Adjustment
|
||||
|
||||
* `LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent`
|
||||
* `LoanBalanceChangedBusinessEvent`
|
||||
|
||||
=== Reversal
|
||||
|
||||
* `LoanAdjustTransactionBusinessEvent`
|
||||
|
||||
== Database Structure
|
||||
|
||||
=== Configuration
|
||||
|
||||
==== Stored on Loan Product (`m_product_loan`)
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Field |Data Type |Description
|
||||
|
||||
|`enable_income_capitalization` |`BOOLEAN` |Enable capitalized income feature (default: `false`)
|
||||
|`capitalized_income_calculation_type` |`VARCHAR` |Calculation method (ENUM: `FLAT`)
|
||||
|`capitalized_income_strategy` |`VARCHAR` |Amortization strategy (ENUM: `EQUAL_AMORTIZATION`)
|
||||
|`capitalized_income_type` |`VARCHAR` |Income type (ENUM: `FEE`, `INTEREST`)
|
||||
|===
|
||||
|
||||
==== Stored on Loan (`m_loan`)
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Field |Data Type |Description
|
||||
|
||||
|`enable_income_capitalization` |`BOOLEAN` |Enable capitalized income feature (default: `false`)
|
||||
|`capitalized_income_calculation_type` |`VARCHAR` |Calculation method (ENUM: `FLAT`)
|
||||
|`capitalized_income_strategy` |`VARCHAR` |Amortization strategy (ENUM: `EQUAL_AMORTIZATION`)
|
||||
|`capitalized_income_type` |`VARCHAR` |Income type (ENUM: `FEE`, `INTEREST`)
|
||||
|===
|
||||
|
||||
=== Balances
|
||||
|
||||
==== On Loan
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Field |Data Type |Description
|
||||
|
||||
|`capitalized_income_derived` |`DECIMAL(19,6)` |Total capitalized income amount (nullable)
|
||||
|`capitalized_income_adjustment_derived` |`DECIMAL(19,6)` |Total adjustment amount (nullable)
|
||||
|===
|
||||
|
||||
==== Capitalized Income Balance (`m_loan_capitalized_income_balance`)
|
||||
|
||||
Each capitalized income has its own balance (1 row for each transaction)
|
||||
|
||||
[cols="3*"]
|
||||
|===
|
||||
|Field |Data Type |Description
|
||||
|
||||
|`id` |`BIGINT` |Unique identifier (Primary Key)
|
||||
|`version` |`BIGINT` |Version for optimistic locking
|
||||
|`loan_id` |`BIGINT` |Associated loan ID (Foreign Key, NOT NULL)
|
||||
|`loan_transaction_id` |`BIGINT` |Associated loan transaction ID (Foreign Key, NOT NULL)
|
||||
|`amount` |`DECIMAL(19,6)` |Capitalized income transaction amount (NOT NULL)
|
||||
|`date` |`DATE` |Capitalized income transaction date (NOT NULL)
|
||||
|`unrecognized_amount` |`DECIMAL(19,6)` |Amortization - not yet recognized amount (NOT NULL)
|
||||
|`charged_off_amount` |`DECIMAL(19,6)` |Charged-off balance (nullable)
|
||||
|`amount_adjustment` |`DECIMAL(19,6)` |Total adjustment amount (nullable)
|
||||
|`created_by` |`BIGINT` |Audit field - user who created the record
|
||||
|`created_on_utc` |`DATETIME` |Creation timestamp (UTC)
|
||||
|`last_modified_by` |`BIGINT` |Last modifier user ID
|
||||
|`last_modified_on_utc` |`DATETIME` |Last modification timestamp (UTC)
|
||||
|===
|
||||
|
||||
=== Constraints
|
||||
|
||||
* **Foreign Key Constraints:**
|
||||
- `loan_id` references `m_loan(id)`
|
||||
- `loan_transaction_id` references `m_loan_transaction(id)`
|
||||
- `created_by` references `m_appuser(id)`
|
||||
- `last_modified_by` references `m_appuser(id)`
|
||||
|
||||
== Notes
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
* Capitalized income transactions support backdating
|
||||
* Adjustment transactions must not predate the original capitalized income
|
||||
* No automatic reversal is supported; must be handled manually via dedicated transactions
|
||||
* Proper GL accounts must be set for Deferred Income and Income from Capitalization to enable this functionality
|
||||
====
|
||||
@@ -0,0 +1,5 @@
|
||||
= Features
|
||||
|
||||
This section covers specific features and functionality available in Apache Fineract.
|
||||
|
||||
include::capitalized-income.adoc[leveloffset=+1]
|
||||
@@ -40,6 +40,8 @@ include::chapters/deployment/index.adoc[leveloffset=+1]
|
||||
|
||||
include::chapters/architecture/index.adoc[leveloffset=+1]
|
||||
|
||||
include::chapters/features/index.adoc[leveloffset=+1]
|
||||
|
||||
include::chapters/development/index.adoc[leveloffset=+1]
|
||||
|
||||
include::chapters/custom/index.adoc[leveloffset=+1]
|
||||
|
||||
Reference in New Issue
Block a user