diff --git a/fineract-doc/src/docs/en/chapters/features/capitalized-income.adoc b/fineract-doc/src/docs/en/chapters/features/capitalized-income.adoc new file mode 100644 index 000000000..a336f90a8 --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/features/capitalized-income.adoc @@ -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 +==== diff --git a/fineract-doc/src/docs/en/chapters/features/index.adoc b/fineract-doc/src/docs/en/chapters/features/index.adoc new file mode 100644 index 000000000..6f50ff171 --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/features/index.adoc @@ -0,0 +1,5 @@ += Features + +This section covers specific features and functionality available in Apache Fineract. + +include::capitalized-income.adoc[leveloffset=+1] \ No newline at end of file diff --git a/fineract-doc/src/docs/en/index.adoc b/fineract-doc/src/docs/en/index.adoc index 325360b5e..b73c684d3 100644 --- a/fineract-doc/src/docs/en/index.adoc +++ b/fineract-doc/src/docs/en/index.adoc @@ -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]