Cosmic Module

O

Qubits of DPK

March 20, 2026

Core Open Source

The Analogy

A loan in Fineract is like a patient in a hospital. It enters (application), gets diagnosed and approved, gets treated (disbursed), goes through follow-ups (repayments), and eventually gets discharged (closed). At every stage, its STATUS changes and different departments handle it.

Loan Status State Machine

javascript
QUBITS OF DPK
1                 ┌────────────────────────────────────┐
2REJECTED (500)3                 └──────────────▲─────────────────────┘
4                                |
5SUBMITTED (100) ──approve──▶ APPROVED (200)
6     |                            |
7   reject                      disburse
8     |                            |
9     ▼                            ▼
10 REJECTED (500)           ACTIVE (300) ─────────┐
11                                |                    |
12                          full repayment         write-off
13                                |                    |
14                                ▼                    ▼
15                           CLOSED (600)       WRITTEN-OFF (601)
Status codes in DB (m_loan.loan_status_id):

Step 1 — Loan Application (Submit)

What Happens

A customer applies for a loan. The loan officer enters details: amount, term, interest rate, product type.

API Call

javascript
QUBITS OF DPK
1POST /fineract-provider/api/v1/loans
2Body: {
3  "clientId": 1,
4  "productId": 1,
5  "principal": 100000,
6  "loanTermFrequency": 12,
7  "loanTermFrequencyType": 2,  // months
8  "interestRatePerPeriod": 12, // 12% per annum
9  "submittedOnDate": "01 March 2025"
10}

Code Flow

javascript
QUBITS OF DPK
1LoanApiResource.calculateLoanScheduleOrSubmitLoanApplication()
2LoanApplicationWritePlatformServiceImpl.submitLoanApplication()
3Validates client exists, product exists, amounts valid
4Creates Loan entity with status = SUBMITTED (100)
5Generates initial repayment schedule
6Saves to m_loan table
7Publishes LoanCreatedBusinessEvent

Key DB Tables

sql
QUBITS OF DPK
1m_loan           -- the loan itself
2m_loan_repayment_schedule  -- instalment breakdown
3m_loan_charge    -- fees attached to this loan

Step 2 — Loan Approval

What Happens

A senior loan officer reviews and approves. Sets the approved principal amount.

API Call

javascript
QUBITS OF DPK
1POST /fineract-provider/api/v1/loans/1?command=approve
2Body: {
3  "approvedOnDate": "03 March 2025",
4  "approvedLoanAmount": 100000
5}

Code Flow

javascript
QUBITS OF DPK
1LoanApiResource.stateTransitions() with command="approve"
2LoanApprovalCommandHandler.handle()
3LoanWritePlatformServiceImpl.approveLoanApplication()
4Validates: loan must be in SUBMITTED state
5Updates loan status = APPROVED (200)
6Records approvedBy (staff ID), approvedOnDate
7Publishes LoanApprovedBusinessEvent

Step 3 — Loan Disbursal

What Happens

Money is actually given to the customer. This is the most critical step — it triggers accounting journal entries.

API Call

javascript
QUBITS OF DPK
1POST /fineract-provider/api/v1/loans/1?command=disburse
2Body: {
3  "actualDisbursementDate": "05 March 2025",
4  "transactionAmount": 100000
5}

Code Flow

javascript
QUBITS OF DPK
1LoanApiResource.stateTransitions() with command="disburse"
2LoanDisbursalCommandHandler.handle()
3LoanWritePlatformServiceImpl.disburseLoan()
4Validates: loan must be APPROVED
5Updates loan status = ACTIVE (300)
6Creates LoanTransaction (type=DISBURSEMENT)
7Builds final repayment schedule with actual dates
8Saves to m_loan_transaction
9Publishes LoanDisbursalBusinessEvent
10AccountingListener creates journal entries:
11                 DEBIT  Loans Receivable +100000
12                 CREDIT Bank Account     -100000

Step 4 — Repayment Schedule

What It Is

When a loan is disbursed, Fineract generates a schedule of instalments. For a ₹1,00,000 loan at 12% for 12 months:
Stored in m_loan_repayment_schedule table.

Repayment Schedule Engine

java
QUBITS OF DPK
1// LoanScheduleGenerator generates this table
2LoanScheduleModel schedule = loanScheduleGenerator.generate(
3    mc,                    // MathContext for rounding
4    loanApplicationTerms,  // amount, interest rate, term
5    loanCharges,           // any fees to include
6    holidayDetailDTO       // bank holidays to skip
7);

Step 5 — Making Repayments

API Call

javascript
QUBITS OF DPK
1POST /fineract-provider/api/v1/loans/1/transactions?command=repayment
2Body: {
3  "transactionDate": "05 April 2025",
4  "transactionAmount": 8920
5}

Code Flow

javascript
QUBITS OF DPK
1LoanTransactionsApiResource.executeLoanTransaction()
2LoanRepaymentCommandHandler.handle()
3LoanWritePlatformServiceImpl.makeLoanRepayment()
4Loads Loan entity
5Applies payment: reduces outstanding principal
6Marks instalment as COMPLETED in schedule
7Creates LoanTransaction (type=REPAYMENT)
8Checks if fully repaid → if yes, closes loan (status=600)
9Publishes LoanRepaymentMadeBusinessEvent
10AccountingListener creates:
11                 DEBIT  Bank Account       +8920
12                 CREDIT Loans Receivable   -7920  (principal portion)
13                 CREDIT Interest Income    -1000  (interest portion)

Repayment Allocation Strategy

Fineract applies payments in this order by default:
  1. #
    Penalties (late fees) first
  2. #
    Fees (processing charges)
  3. #
    Interest
  4. #
    Principal
This order can be configured per loan product.

Step 6 — Delinquency

What It Is

If a customer misses a repayment, the loan becomes delinquent (overdue). Fineract classifies delinquency into buckets:

How It's Detected

The COB (Close of Business) batch job runs nightly and:
  1. #
    Checks every active loan's repayment schedule
  2. #
    If today > due date and not paid → marks as overdue
  3. #
    Calculates days past due
  4. #
    Assigns to appropriate delinquency bucket
  5. #
    Applies penalty charges if configured

Step 7 — Loan Closure

Natural Closure (Full Repayment)

When the last instalment is paid, Fineract automatically:
  • Sets loan status = CLOSED (600)
  • Sets closedOnDate
  • Publishes LoanClosedBusinessEvent

Write-Off

If the bank gives up:
javascript
QUBITS OF DPK
1POST /loans/1/transactions?command=writeoff
2  → status = WRITTEN-OFF (601)
3Outstanding balance moved to Bad Debt Expense (accounting)

Key Domain Classes

Mentor Cheat Sheet