Skip to main content

Loan Disbursement

Complete guide to loan disbursement transactions in BankLingo V2, including fund release, schedule activation, and fee handling.


Overview​

A Loan Disbursement Transaction releases approved loan funds to the borrower's account. This transaction activates the loan account and its repayment schedule.

Key Characteristics:

  • Prerequisites: Loan must be APPROVED, schedules generated
  • Fund Release: Transfer from loan disbursement account to customer account
  • Schedule Activation: Marks loan schedules as ACTIVE
  • Fee Deduction: Processing fees deducted at disbursement
  • GL Integration: Multiple GL postings (loan asset, deposits, fees, settlement)
  • Partial Disbursement: Support for tranched disbursements

Disbursement Methods:

  • Direct to Account: Credit to customer's deposit account
  • Cash Disbursement: Cash via teller till
  • Third-Party Payment: Direct payment to vendor/supplier
  • Tranche Disbursement: Multiple partial disbursements over time

Transaction Flow​

Entities Impacted​

Scenario 1: Full Disbursement to Customer Account​

Disburse NGN 1,000,000 loan with 2% processing fee

COMPLETED Phase:

{
transactionId: "DISB123456",
transactionState: "COMPLETED",
disbursementAmount: 1000000.00,
processingFee: 20000.00, // 2% of 1,000,000
netDisbursement: 980000.00,
impactedEntities: [
// Loan Account
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "State",
oldValue: "APPROVED",
newValue: "ACTIVE",
deltaAmount: 0
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "PrincipalBalance",
oldValue: 0.00,
newValue: 1000000.00,
deltaAmount: +1000000.00
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "DisbursedAmount",
oldValue: 0.00,
newValue: 1000000.00,
deltaAmount: +1000000.00
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "DisbursementDate",
oldValue: null,
newValue: "2025-12-28T10:00:00Z",
deltaAmount: 0
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "FirstRepaymentDate",
oldValue: null,
newValue: "2026-01-28",
deltaAmount: 0
},
// Loan Schedules (all activated)
{
entityType: "LoanSchedule",
entityId: 201,
entityKey: "SCH-001-01",
fieldName: "State",
oldValue: "PENDING",
newValue: "ACTIVE",
deltaAmount: 0
},
{
entityType: "LoanSchedule",
entityId: 202,
entityKey: "SCH-001-02",
fieldName: "State",
oldValue: "PENDING",
newValue: "ACTIVE",
deltaAmount: 0
},
// ... (all 12 schedules activated)

// Customer Deposit Account (net amount after fees)
{
entityType: "DepositAccount",
entityId: 456,
entityKey: "ACC-CUST-001",
fieldName: "AvailableBalance",
oldValue: 50000.00,
newValue: 1030000.00, // 50000 + 980000
deltaAmount: +980000.00
},
{
entityType: "DepositAccount",
entityId: 456,
entityKey: "ACC-CUST-001",
fieldName: "BookBalance",
oldValue: 50000.00,
newValue: 1030000.00,
deltaAmount: +980000.00
},
// Loan Disbursement Account
{
entityType: "DepositAccount",
entityId: 999,
entityKey: "ACC-DISB-POOL",
fieldName: "AvailableBalance",
oldValue: 10000000.00,
newValue: 9000000.00,
deltaAmount: -1000000.00
},
{
entityType: "DepositAccount",
entityId: 999,
entityKey: "ACC-DISB-POOL",
fieldName: "BookBalance",
oldValue: 10000000.00,
newValue: 9000000.00,
deltaAmount: -1000000.00
},
// GL Accounts
{
entityType: "GLAccount",
entityKey: "3100-001", // Loans to Customers (Asset)
fieldName: "DebitAmount",
deltaAmount: +1000000.00
},
{
entityType: "GLAccount",
entityKey: "1050-001", // Loan Disbursement Account
fieldName: "CreditAmount",
deltaAmount: +1000000.00
},
{
entityType: "GLAccount",
entityKey: "1050-001", // Loan Disbursement Account
fieldName: "DebitAmount",
deltaAmount: +980000.00
},
{
entityType: "GLAccount",
entityKey: "2100-001", // Customer Deposits Liability
fieldName: "CreditAmount",
deltaAmount: +980000.00
},
{
entityType: "GLAccount",
entityKey: "4200-001", // Loan Processing Fee Income
fieldName: "CreditAmount",
deltaAmount: +20000.00
}
]
}
```text
### Scenario 2: Partial Disbursement (Tranche 1 of 2)

**Disburse NGN 500,000 (first tranche of NGN 1,000,000 loan)**

```typescript
{
transactionId: "DISB234567",
transactionState: "COMPLETED",
loanAmount: 1000000.00,
trancheNumber: 1,
trancheAmount: 500000.00,
processingFee: 10000.00, // 2% of tranche
netDisbursement: 490000.00,
impactedEntities: [
// Loan Account
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "State",
oldValue: "APPROVED",
newValue: "ACTIVE_PARTIAL", // Partially disbursed
deltaAmount: 0
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "PrincipalBalance",
oldValue: 0.00,
newValue: 500000.00,
deltaAmount: +500000.00
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "DisbursedAmount",
oldValue: 0.00,
newValue: 500000.00,
deltaAmount: +500000.00
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "UndisbursedAmount",
oldValue: 1000000.00,
newValue: 500000.00,
deltaAmount: -500000.00
},
// Loan Schedules (proportionally activated)
{
entityType: "LoanSchedule",
entityId: 201,
entityKey: "SCH-001-01",
fieldName: "State",
oldValue: "PENDING",
newValue: "ACTIVE",
deltaAmount: 0
},
{
entityType: "LoanSchedule",
entityId: 201,
entityKey: "SCH-001-01",
fieldName: "PrincipalDue",
oldValue: 83333.33, // Original for 1M
newValue: 41666.67, // Adjusted for 500K
deltaAmount: -41666.66
},
// Customer Deposit Account
{
entityType: "DepositAccount",
entityId: 456,
entityKey: "ACC-CUST-001",
fieldName: "AvailableBalance",
oldValue: 50000.00,
newValue: 540000.00,
deltaAmount: +490000.00
},
// GL Accounts
{
entityType: "GLAccount",
entityKey: "3100-001", // Loans to Customers
fieldName: "DebitAmount",
deltaAmount: +500000.00
},
{
entityType: "GLAccount",
entityKey: "4200-001", // Fee Income
fieldName: "CreditAmount",
deltaAmount: +10000.00
}
]
}
```text
### Scenario 3: Cash Disbursement via Teller

**Disburse NGN 200,000 loan as cash**

```typescript
{
transactionId: "DISB345678",
transactionState: "COMPLETED",
disbursementAmount: 200000.00,
processingFee: 4000.00,
netDisbursement: 196000.00,
disbursementMethod: "CASH",
impactedEntities: [
// Loan Account
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "State",
oldValue: "APPROVED",
newValue: "ACTIVE",
deltaAmount: 0
},
{
entityType: "LoanAccount",
entityId: 100,
entityKey: "LOAN-001",
fieldName: "PrincipalBalance",
oldValue: 0.00,
newValue: 200000.00,
deltaAmount: +200000.00
},
// Teller Till (cash disbursed)
{
entityType: "TellerTill",
entityId: 789,
entityKey: "TILL-001",
fieldName: "CashBalance",
oldValue: 500000.00,
newValue: 304000.00, // 500000 - 196000
deltaAmount: -196000.00
},
{
entityType: "TellerTill",
entityId: 789,
entityKey: "TILL-001",
fieldName: "TransactionCount",
oldValue: 45,
newValue: 46,
deltaAmount: +1
},
// GL Accounts
{
entityType: "GLAccount",
entityKey: "3100-001", // Loans to Customers
fieldName: "DebitAmount",
deltaAmount: +200000.00
},
{
entityType: "GLAccount",
entityKey: "1010-001", // Cash in Till
fieldName: "CreditAmount",
deltaAmount: +196000.00
},
{
entityType: "GLAccount",
entityKey: "4200-001", // Fee Income
fieldName: "CreditAmount",
deltaAmount: +4000.00
}
]
}
```text
---

## Disbursement States

:::warning[Code Removed]
**Implementation details removed for security.**

Contact support for implementation guidance.
:::text
### State Transitions

```text
PENDING Ò”€Ò”€Ò”¬Ò”€Ò”€Ò–º APPROVED Ò”€Ò”€Ò–º COMPLETED Ò”€Ò”€Ò–º ACTIVE
Γ’β€β€š Γ’β€β€š
Ò””Ò”€Ò”€Ò–º REJECTED Ò””Ò”€Ò”€Ò–º FAILED

ACTIVE Ò”€Ò”€Ò–º REVERSED (special case for early reversal)
```text
---

## Fee Calculation

### Processing Fee

```yaml
processingFees:
- loanProductId: "PERSONAL_LOAN"
feeType: PERCENTAGE
percentage: 2.0
minAmount: 5000.00
maxAmount: 50000.00
deductionMethod: UPFRONT # Deducted at disbursement

- loanProductId: "SME_LOAN"
feeType: TIERED
tiers:
- minAmount: 0
maxAmount: 500000
fee: 10000.00
- minAmount: 500001
maxAmount: 2000000
fee: 25000.00
- minAmount: 2000001
maxAmount: null
fee: 50000.00
deductionMethod: UPFRONT

Insurance Premium​

insurancePremium:
- loanProductId: "PERSONAL_LOAN"
premiumType: PERCENTAGE
percentage: 1.5
deductionMethod: UPFRONT
insuranceProvider: "XYZ Insurance"

Fee Calculation Logic​

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.


Validation Rules​

RuleCheckError CodeMessage
Loan ApprovedLoanAccount.State is APPROVED05Loan not approved
Schedules GeneratedLoanSchedules.Count > 030Loan schedules not generated
Not Already DisbursedLoanAccount.DisbursedAmount is 031Loan already disbursed
Valid AmountDisbursementAmount not exceeds ApprovedAmount12Exceeds approved amount
Customer Account ActiveDestAccount.State is ACTIVE14Customer account not active
Disbursement Account BalanceDisbAccount.Balance sufficient for DisbursementAmount51Insufficient funds in disbursement pool
Tranche ValidationIf tranche: Amount not exceeds UndisbursedAmount32Exceeds undisbursed amount
Collateral VerifiedIf required: Collateral.Status is VERIFIED33Collateral not verified

GL Posting​

Journal Entries​

Example 1: Full Disbursement to Account

AccountDescriptionDebitCredit
3100-001Loans to Customers (Asset)1,000,000.00-
1050-001Loan Disbursement Account-1,000,000.00
1050-001Loan Disbursement Account980,000.00-
2100-001Customer Deposits-980,000.00
4200-001Loan Processing Fee Income-20,000.00

Example 2: Cash Disbursement

AccountDescriptionDebitCredit
3100-001Loans to Customers200,000.00-
1010-001Cash in Till-196,000.00
4200-001Fee Income-4,000.00

Example 3: Third-Party Payment (Vendor)

AccountDescriptionDebitCredit
3100-001Loans to Customers500,000.00-
2200-001Payable to Vendor-490,000.00
4200-001Fee Income-10,000.00

API Usage​

Request​

POST /api/v2/transactions/loan/disburse
Content-Type: application/json
Authorization: Bearer {token}

{
"loanAccountEncodedKey": "LOAN-001",
"disbursementAmount": 1000000.00,
"disbursementMethod": "TO_ACCOUNT",
"destinationAccountEncodedKey": "ACC-CUST-001",
"disbursementDate": "2025-12-28",
"notes": "Full disbursement - personal loan",
"channelType": "BRANCH",
"tellerId": 123,
"isTranche": false
}
```text
### Response - Success (Auto-Approved)

```json
{
"success": true,
"transactionKey": "DISB123456",
"disbursementId": 789012,
"state": "COMPLETED",
"loanAccountNumber": "LN-0001",
"loanAccountName": "John Doe",
"disbursementAmount": 1000000.00,
"processingFee": 20000.00,
"insurancePremium": 15000.00,
"netDisbursement": 965000.00,
"destinationAccount": "0123456789",
"destinationAccountNewBalance": 1015000.00,
"loanState": "ACTIVE",
"principalBalance": 1000000.00,
"firstRepaymentDate": "2026-01-28",
"schedulesActivated": 12,
"disbursementDate": "2025-12-28T10:00:00Z",
"impactRecordId": 456,
"message": "Loan disbursed successfully"
}
```text
### Response - Pending Approval

```json
{
"success": true,
"transactionKey": "DISB123457",
"disbursementId": 789013,
"state": "PENDING",
"loanAccountNumber": "LN-0001",
"disbursementAmount": 5000000.00,
"approvalRequired": true,
"message": "Disbursement pending approval. Amount exceeds auto-approval limit."
}
```text
### Response - Error (Loan Not Approved)

```json
{
"success": false,
"errorCode": "05",
"errorMessage": "Loan not approved",
"loanAccountNumber": "LN-0001",
"currentState": "PENDING_APPROVAL",
"requiredState": "APPROVED"
}
```text
---

## Error Codes

| Code | Message | Cause | Resolution |
|------|---------|-------|------------|
| 00 | Success | Disbursement completed | None |
| 05 | Loan not approved | Loan state != APPROVED | Complete loan approval |
| 12 | Invalid amount | Amount is 0 or negative or exceeds approved | Correct amount |
| 14 | Invalid account | Destination account invalid | Verify account |
| 30 | Schedules not generated | No loan schedules exist | Generate schedules |
| 31 | Already disbursed | Loan already has disbursement | Check loan status |
| 32 | Exceeds undisbursed amount | Tranche > remaining | Reduce tranche amount |
| 33 | Collateral not verified | Required collateral missing | Complete collateral verification |
| 51 | Insufficient disbursement funds | Disbursement pool balance low | Fund disbursement account |
| 91 | System error | Database/network failure | Retry or contact support |

---

## Schedule Activation

### Full Disbursement

:::warning[Code Removed]
**Implementation details removed for security.**

Contact support for implementation guidance.
:::text
### Partial Disbursement (Tranche)

:::warning[Code Removed]
**Implementation details removed for security.**

Contact support for implementation guidance.
:::text
---

## Best Practices

### For Loan Officers

1. **Verify Approval**: Ensure loan is fully approved before disbursement
2. **Check Documents**: Verify all required documents are complete
3. **Confirm Account**: Verify destination account belongs to customer
4. **Review Fees**: Explain all fees to customer before disbursement
5. **Schedule Review**: Review repayment schedule with customer
6. **Disbursement Method**: Confirm preferred disbursement method
7. **Documentation**: Document disbursement in loan file

### For Developers

1. **Atomic Operation**: Wrap entire disbursement in database transaction
2. **Track All Impacts**: Record loan account, schedules, deposit account, GL
3. **Fee Calculation**: Apply all fees before crediting customer
4. **Schedule Activation**: Ensure all schedules activated atomically
5. **GL Consistency**: Verify debits equal credits
6. **Idempotency**: Prevent duplicate disbursements with unique keys
7. **Audit Trail**: Log all state changes and amounts

### For Operations

1. **Monitor Disbursement Pool**: Ensure sufficient balance
2. **Review Large Disbursements**: Flag disbursements > threshold
3. **Reconcile Daily**: Match disbursements to GL postings
4. **Track Failures**: Investigate failed disbursements immediately
5. **Verify Schedules**: Confirm schedules activated correctly

---

## Implementation Checklist

### Database Requirements

- [ ] Create `LoanDisbursement` table
- [ ] Add columns: State, DisbursementAmount, ProcessingFee, NetDisbursement, DisbursementMethod
- [ ] Add `DisbursedAmount` and `UndisbursedAmount` to LoanAccount
- [ ] Add `ActivationDate` to LoanSchedule
- [ ] Create `TransactionImpactRecord` and `ImpactedEntity` tables
- [ ] Add index on `LoanDisbursement(LoanAccountId, State)`

### Code Requirements

- [ ] Implement `ProcessLoanDisbursementAsync` with impact tracking
- [ ] Add fee calculation (processing fee, insurance)
- [ ] Implement schedule activation logic
- [ ] Add support for partial/tranche disbursements
- [ ] Implement GL posting for disbursements
- [ ] Add validation for loan state and schedules
- [ ] Create background job for failed disbursement retry

### Testing Requirements

- [ ] Unit test: Full disbursement with fee deduction
- [ ] Unit test: Partial disbursement (tranche)
- [ ] Unit test: Schedule activation
- [ ] Integration test: End-to-end disbursement to account
- [ ] Integration test: Cash disbursement via teller
- [ ] Integration test: Disbursement + reversal
- [ ] Performance test: 100 concurrent disbursements

### Documentation Requirements

- [ ] Update API documentation with disbursement endpoints
- [ ] Create loan officer guide on disbursement process
- [ ] Document fee structures by product
- [ ] Create runbook for failed disbursements

---

**Next**: [Loan Repayment Ò†’](./repayment)

## Developer Resources

For API implementation details, see:
- [Loan Transactions API Reference](../../developer/)
- [Loan Transactions Developer Guide](../../developer/overview)

---