Skip to main content

Loan Write-Off Transaction

Overview

A Loan Write-Off is a transaction where a bank removes a non-performing loan from its active loan portfolio and recognizes it as a bad debt loss. This is a critical accounting transaction that reflects the bank's recognition that the loan is uncollectible, though it does not legally release the borrower from the debt obligation.

Key Characteristics

  • Purpose: Remove uncollectible loans from active portfolio and recognize loss
  • Transaction Type: WROF (Write-Off)
  • Complexity: High - Regulatory requirements, provisioning, GL adjustments
  • Multi-Entity Impact: LoanAccount, all LoanSchedules, GL accounts (provision, write-off expense)
  • Critical Requirement: Accurate provisioning and regulatory compliance

Write-Off vs Loan Closure

AspectWrite-OffPayoff/Closure
Borrower ObligationStill legally liable (can pursue collection)Fully discharged
Loan StateWRITTEN_OFF (special state)CLOSED
Payment SourceBank provisions/reservesCustomer payment
GL ImpactExpense recognition (loss)Asset reduction
RegulatoryNPL removal, capital impactNormal closure
CollectionCan continue post write-offNo further collection

Critical: A write-off is an accounting transaction, not a legal forgiveness. The bank can still pursue collection, and if recovered, the amount is recognized as recovery income.


Transaction Flow


Entities Impacted

Scenario 1: Fully Provisioned Write-Off

Context:

  • Loan: NGN 2,000,000 principal, 22% annual interest
  • Days Past Due: 365 days (1 year overdue)
  • Outstanding Principal: NGN 1,800,000 (10% paid before default)
  • Accrued Interest: NGN 396,000 (unpaid)
  • Unpaid Penalties: NGN 45,000 (arrears penalties)
  • Unpaid Fees: NGN 12,000 (late payment fees)
  • Provision Balance: NGN 2,253,000 (100% provisioned)
  • Collection Efforts: Exhausted (12 months, no recovery)

Write-Off Calculation:

WriteOffAmount = 1,800,000 (principal) 
+ 396,000 (interest)
+ 45,000 (penalties)
+ 12,000 (fees)
= 2,253,000

ProvisionUsed = 2,253,000 (100% covered)
AdditionalExpense = 0 (fully provisioned)
```text
**Entities Impacted** (22 ImpactedEntity records):

```typescript
{
transactionId: "WROF-001",
transactionType: "Loan Write-Off",
writeOffAmount: 2253000.00,
writeOffDate: "2025-12-28",
writeOffReason: "Non-performing loan, 365 days overdue, collection efforts exhausted",
impactedEntities: [

// 1-8: LoanSchedule impacts (8 outstanding schedules)
// Schedule 29 (example)
{
entityType: "LoanSchedule",
entityId: 329,
entityKey: "SCH-329",
fieldName: "PrincipalDue",
oldValue: 225000.00,
newValue: 225000.00, // Unchanged (still due legally)
deltaAmount: 0
},
{
entityType: "LoanSchedule",
entityId: 329,
fieldName: "OutstandingBalance",
oldValue: 225000.00,
newValue: 225000.00, // Tracked for collection
deltaAmount: 0
},
{
entityType: "LoanSchedule",
entityId: 329,
fieldName: "State",
oldValue: "OVERDUE",
newValue: "WRITTEN_OFF",
deltaAmount: 0
},
{
entityType: "LoanSchedule",
entityId: 329,
fieldName: "WriteOffDate",
oldValue: null,
newValue: "2025-12-28",
deltaAmount: 0
},
// ... (7 more schedules, each with 4 field changes = 28 records)

// 9-16: LoanAccount impacts
{
entityType: "LoanAccount",
entityId: 102,
entityKey: "LOAN-102",
fieldName: "PrincipalBalance",
oldValue: 1800000.00,
newValue: 0.00, // Removed from active portfolio
deltaAmount: -1800000.00
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "InterestBalance",
oldValue: 396000.00,
newValue: 0.00,
deltaAmount: -396000.00
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "PenaltyBalance",
oldValue: 45000.00,
newValue: 0.00,
deltaAmount: -45000.00
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "FeesBalance",
oldValue: 12000.00,
newValue: 0.00,
deltaAmount: -12000.00
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "WriteOffAmount",
oldValue: 0.00,
newValue: 2253000.00,
deltaAmount: +2253000.00
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "WriteOffDate",
oldValue: null,
newValue: "2025-12-28",
deltaAmount: 0
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "LoanState",
oldValue: "OVERDUE",
newValue: "WRITTEN_OFF",
deltaAmount: 0
},
{
entityType: "LoanAccount",
entityId: 102,
fieldName: "LastCollectionAttemptDate",
oldValue: "2025-12-15",
newValue: "2025-12-15",
deltaAmount: 0
},

// 17-22: GL Account impacts
{
entityType: "GLAccount",
entityKey: "1108-PROVISION-FOR-LOAN-LOSSES",
fieldName: "DebitAmount",
deltaAmount: +2253000.00 // DR: Use provision
},
{
entityType: "GLAccount",
entityKey: "1101-LOANS-TO-CUSTOMERS",
fieldName: "CreditAmount",
deltaAmount: +1800000.00 // CR: Remove loan asset
},
{
entityType: "GLAccount",
entityKey: "1105-INTEREST-RECEIVABLE",
fieldName: "CreditAmount",
deltaAmount: +396000.00 // CR: Write off interest
},
{
entityType: "GLAccount",
entityKey: "1106-FEES-RECEIVABLE",
fieldName: "CreditAmount",
deltaAmount: +12000.00 // CR: Write off fees
},
{
entityType: "GLAccount",
entityKey: "1107-PENALTIES-RECEIVABLE",
fieldName: "CreditAmount",
deltaAmount: +45000.00 // CR: Write off penalties
},
{
entityType: "GLAccount",
entityKey: "NPL-REGISTER-OFF-BALANCE-SHEET",
fieldName: "MemoEntry",
deltaAmount: +2253000.00 // Track for collection
}
]
}
```text
**Note**: Outstanding amounts remain tracked for collection purposes but removed from balance sheet.

---

### Scenario 2: Under-Provisioned Write-Off

**Context**:
- Loan: NGN 500,000 principal, 18% annual interest
- Days Past Due: 180 days (6 months overdue)
- Outstanding Principal: NGN 480,000 (4% paid)
- Accrued Interest: NGN 86,400 (unpaid)
- Unpaid Penalties: NGN 15,000
- Unpaid Fees: NGN 5,000
- **Provision Balance**: NGN 400,000 (only 68% provisioned)
- Write-Off Decision: Management decision due to legal issues

**Write-Off Calculation**:

```typescript
WriteOffAmount = 480,000 (principal)
+ 86,400 (interest)
+ 15,000 (penalties)
+ 5,000 (fees)
= 586,400

ProvisionUsed = 400,000 (from provision)
AdditionalExpense = 186,400 (under-provisioned - additional P&L hit)
```text
**Key GL Impacts**:

```typescript
{
transactionId: "WROF-002",
writeOffAmount: 586400.00,
provisionUsed: 400000.00,
additionalExpense: 186400.00,
impactedEntities: [

// LoanAccount impacts
{
entityType: "LoanAccount",
entityId: 103,
fieldName: "PrincipalBalance",
oldValue: 480000.00,
newValue: 0.00,
deltaAmount: -480000.00
},
{
entityType: "LoanAccount",
entityId: 103,
fieldName: "WriteOffAmount",
oldValue: 0.00,
newValue: 586400.00,
deltaAmount: +586400.00
},
{
entityType: "LoanAccount",
entityId: 103,
fieldName: "LoanState",
oldValue: "OVERDUE",
newValue: "WRITTEN_OFF",
deltaAmount: 0
},

// GL impacts - TWO expense sources
{
entityType: "GLAccount",
entityKey: "1108-PROVISION-FOR-LOAN-LOSSES",
fieldName: "DebitAmount",
deltaAmount: +400000.00 // DR: Use existing provision
},
{
entityType: "GLAccount",
entityKey: "5201-BAD-DEBT-EXPENSE",
fieldName: "DebitAmount",
deltaAmount: +186400.00 // DR: Additional expense (P&L hit)
},
{
entityType: "GLAccount",
entityKey: "1101-LOANS-TO-CUSTOMERS",
fieldName: "CreditAmount",
deltaAmount: +480000.00 // CR: Remove principal
},
{
entityType: "GLAccount",
entityKey: "1105-INTEREST-RECEIVABLE",
fieldName: "CreditAmount",
deltaAmount: +86400.00 // CR: Write off interest
},
{
entityType: "GLAccount",
entityKey: "1106-FEES-RECEIVABLE",
fieldName: "CreditAmount",
deltaAmount: +5000.00 // CR: Write off fees
},
{
entityType: "GLAccount",
entityKey: "1107-PENALTIES-RECEIVABLE",
fieldName: "CreditAmount",
deltaAmount: +15000.00 // CR: Write off penalties
}
]
}
```text
**Critical Impact**: Under-provisioned write-off directly hits P&L with additional expense of NGN 186,400.

---

## Write-Off Implementation

### Step 1: Validate Write-Off Eligibility

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

Contact support for implementation guidance.
:::text
### Step 2: Calculate Write-Off Amount

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

Contact support for implementation guidance.
:::text
### Step 3: Execute Write-Off Transaction

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

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

## GL Posting Examples

### Example 1: Fully Provisioned Write-Off

**Transaction**: Write-off NGN 2,253,000 (fully provisioned)

```text
DR: 1108-PROVISION-FOR-LOAN-LOSSES 2,253,000.00 (Use provision)
CR: 1101-LOANS-TO-CUSTOMERS 1,800,000.00 (Remove principal)
CR: 1105-INTEREST-RECEIVABLE 396,000.00 (Write off interest)
CR: 1106-FEES-RECEIVABLE 12,000.00 (Write off fees)
CR: 1107-PENALTIES-RECEIVABLE 45,000.00 (Write off penalties)

MEMO: NPL-REGISTER (Off Balance Sheet) 2,253,000.00 (Track for collection)
```text
**Net Effect**:

- Assets decrease: 2,253,000 (loans + receivables)
- Provision decrease: 2,253,000 (already expensed in prior periods)
- **No P&L impact** (provision already taken)

### Example 2: Under-Provisioned Write-Off

**Transaction**: Write-off NGN 586,400 (68% provisioned - NGN 400,000)

```text
DR: 1108-PROVISION-FOR-LOAN-LOSSES 400,000.00 (Use existing provision)
DR: 5201-BAD-DEBT-EXPENSE 186,400.00 (Additional expense)
CR: 1101-LOANS-TO-CUSTOMERS 480,000.00 (Remove principal)
CR: 1105-INTEREST-RECEIVABLE 86,400.00 (Write off interest)
CR: 1106-FEES-RECEIVABLE 5,000.00 (Write off fees)
CR: 1107-PENALTIES-RECEIVABLE 15,000.00 (Write off penalties)

MEMO: NPL-REGISTER (Off Balance Sheet) 586,400.00 (Track for collection)
```text
**Net Effect**:

- Assets decrease: 586,400 (loans + receivables)
- Provision decrease: 400,000
- **P&L impact: -186,400** (additional bad debt expense)

---

## Recovery After Write-Off

### Recovery Transaction

If a written-off loan is recovered (partially or fully), it's recognized as **recovery income**:

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

Contact support for implementation guidance.
:::text
**Recovery GL Entry**:

```text
DR: 1001-CASH 100,000.00 (Recovery received)
CR: 4301-RECOVERY-INCOME 100,000.00 (Income recognized)
```text
---

## API Usage

### Request: Validate Write-Off Eligibility

```http
POST /api/v2/loans/{loanAccountId}/writeoff/validate
Content-Type: application/json

{
"writeOffReason": "Non-performing loan, 365 days overdue, exhausted collection"
}
```text
### Response: Eligibility Validation

```json
{
"loanAccountId": 102,
"loanAccountKey": "LOAN-102",
"isEligible": true,
"daysPastDue": 365,
"collectionAttempts": 8,
"outstandingBalance": 2253000.00,
"provisioningBalance": 2253000.00,
"provisioningPercentage": 100.00,
"additionalExpense": 0.00,
"regulatoryRequirement": "MUST write-off per CBN Prudential Guidelines (NPL > 365 days)",
"warnings": [],
"validationErrors": []
}
```text
### Request: Execute Write-Off

```http
POST /api/v2/loans/{loanAccountId}/writeoff/execute
Content-Type: application/json
Authorization: Bearer {token}

{
"writeOffDate": "2025-12-28",
"writeOffReason": "Non-performing loan, 365 days overdue, collection exhausted",
"approvedBy": "CREDIT_COMMITTEE",
"approvalReference": "CC-2025-12-15-089",
"notes": "Borrower declared bankrupt, no collateral value"
}
```text
### Response: Write-Off Executed

```json
{
"transactionId": 456,
"transactionKey": "WROF-001",
"transactionType": "LOAN_WRITE_OFF",
"state": "COMPLETED",
"loanAccountId": 102,
"loanAccountKey": "LOAN-102",
"writeOffAmount": 2253000.00,
"writeOffDate": "2025-12-28",
"completedDate": "2025-12-28T14:20:00Z",
"breakdown": {
"outstandingPrincipal": 1800000.00,
"accruedInterest": 396000.00,
"unpaidPenalties": 45000.00,
"unpaidFees": 12000.00,
"provisionUsed": 2253000.00,
"additionalExpense": 0.00
},
"schedulesWrittenOff": 8,
"loanAccountState": "WRITTEN_OFF",
"regulatoryImpact": {
"nplRemoval": 2253000.00,
"capitalRelief": 112650.00, // 5% of write-off
"provisionRelease": 2253000.00
},
"impactedEntities": [
{
"entityType": "LoanAccount",
"entityId": 102,
"changes": [
{
"fieldName": "PrincipalBalance",
"oldValue": 1800000.00,
"newValue": 0.00,
"delta": -1800000.00
},
{
"fieldName": "State",
"oldValue": "OVERDUE",
"newValue": "WRITTEN_OFF"
}
]
}
]
}
```text
---

## Validation Rules

### Pre-Execution Validation

1. **Loan Account State**:
- Must be `ACTIVE` or `OVERDUE`
- Cannot be `CLOSED`, `CANCELLED`, or already `WRITTEN_OFF`

2. **Days Past Due (Regulatory)**:
- Minimum 180 days (Nigeria CBN guideline)
- Mandatory write-off at 365 days for NPL > 1 year

3. **Collection Efforts**:
- Minimum 3 collection attempts documented
- Legal action considered or exhausted

4. **Approval Requirements**:
- **ALWAYS requires credit committee approval**
- Approval reference must be provided
- Approver authorization verified

5. **Provisioning Check**:
- Calculate provisioning percentage
- Warn if under-provisioned (< 100%)
- Show additional P&L impact

### Post-Execution Validation

6. **All Schedules Written Off**:
- Verify all schedules marked as `WRITTEN_OFF`
- Confirm write-off dates set

7. **Loan Account Zeroed**:
- PrincipalBalance = 0
- InterestBalance = 0
- PenaltyBalance = 0
- FeesBalance = 0
- State = `WRITTEN_OFF`

8. **GL Balancing**:
- Total debits = Total credits
- Provision + Additional Expense = Total Write-Off Amount

9. **Off-Balance Sheet Tracking**:
- Add to NPL Register (memo account)
- Track for collection and recovery

---

## Error Codes

| Code | Description | Resolution |
|------|-------------|------------|
| **WROF_001** | Loan account not found | Verify loan account exists |
| **WROF_002** | Loan already written off | Cannot write off twice |
| **WROF_003** | Loan not overdue | Must be NPL (180+ days) |
| **WROF_004** | Insufficient days past due | Wait for minimum 180 days |
| **WROF_005** | Missing collection efforts | Document collection attempts |
| **WROF_006** | Missing approval | Requires credit committee approval |
| **WROF_007** | Invalid approval reference | Verify approval document |
| **WROF_008** | Provisioning error | Check provisioning account |
| **WROF_009** | GL posting failed | Contact support |
| **WROF_010** | Regulatory compliance failed | Check CBN guidelines |

---

## Best Practices

### For Product Managers

1. **Provisioning Policy**:
- Maintain 100% provisioning for NPL > 180 days
- Incremental provisioning: 50% at 90 days, 100% at 180 days
- Reduces P&L shock at write-off

2. **Write-Off Criteria**:
- Clear policy on when to write off (days, amount thresholds)
- Balance between regulatory requirements and operational needs
- Consider cost of continued collection vs write-off

3. **Collection Post Write-Off**:
- Continue collection efforts (legal liability remains)
- Track recoveries for income recognition
- Maintain off-balance sheet register

### For Developers

1. **Impact Tracking**:
- Track EVERY schedule write-off with state changes
- Store write-off reason and approval reference
- Link provisioning movements to transaction

2. **Regulatory Compliance**:
- Implement CBN prudential guidelines
- Auto-flag loans for write-off at 365 days
- Generate regulatory reports (NPL, provisioning)

3. **Recovery Tracking**:
- Implement recovery transaction type
- Link recoveries to original write-off
- Track collection costs vs recovery amount

### For Bank Operations

1. **Write-Off Process**:
- Document all collection efforts before write-off
- Obtain credit committee approval
- Verify provisioning adequacy
- Generate write-off certificate

2. **Post-Write-Off Management**:
- Transfer to collections/legal department
- Update credit bureau (if applicable)
- Monitor for recovery opportunities
- Maintain off-balance sheet register

3. **Reconciliation**:
- Monthly NPL write-off report
- Verify GL postings vs loan balances
- Track provisioning movements
- Recovery income reconciliation

---

## V2 API Commands

### Architecture Overview

The V2 loan write-off command follows the **Delegation Pattern**:

- **V2 Facade**: `InitiateLoanWriteOffCommand` (BPMCore compatible)
- **V1 Implementation**: Delegates to existing V1 `LoanWriteOffCommand` with full business logic
- **BPM Integration**: Accepts parameters via `BpmUtil.GetPropertyValue()`

**Implementation**: `CB.Administration.Api/Commands/BPMCore/Loans/AdministrationCoreLoanCommandHandlers.Transactions.cs`

---

### InitiateLoanWriteOffCommand

**Purpose**: Write off an unrecoverable loan (non-performing loan closure)

**Command**: `InitiateLoanWriteOffCommand`

**Delegation**: → `LoanWriteOffCommand` (V1 implementation)

#### BPM Parameters

```json
{
"commandName": "InitiateLoanWriteOffCommand",
"data": {
"accountEncodedKey": "string (mandatory)",
"clientEncodedKey": "string (mandatory)",
"writeOffReason": "string (optional)",
"transactionDate": "DateTime (optional)",
"notes": "string (optional)"
}
}

Parameter Details

ParameterTypeRequiredDescription
accountEncodedKeystring✅ YesLoan account to write off
clientEncodedKeystring✅ YesClient/borrower encoded key
writeOffReasonstring❌ NoReason code (e.g., "UNRECOVERABLE")
transactionDateDateTime❌ NoWrite-off date (defaults to today)
notesstring❌ NoWrite-off notes/remarks

Balance Impact

Loan Account:

Balance FieldChangeReason
PrincipalBalance→ 0Written off
InterestBalance→ 0Written off
PenaltyBalance→ 0Written off
FeesBalance→ 0Written off
State→ WRITTEN_OFFLoan closed as NPL
WriteOffDateSet to transaction dateTrack write-off date
WriteOffAmountTotal written offTrack amount

GL Impact: Debits provisioning account, credits loan asset account

Example Response

{
"isSuccessful": true,
"transactionId": "TXN-LOAN-WO-20251229-0001",
"transactionState": "SETTLED",
"message": "Loan written off successfully",
"data": {
"loanAccountKey": "8a8080827f23loan017f23def456",
"writeOffAmount": 450000.00,
"writeOffDate": "2025-12-29T10:00:00Z",
"loanState": "WRITTEN_OFF",
"components": {
"principalWrittenOff": 400000.00,
"interestWrittenOff": 45000.00,
"penaltiesWrittenOff": 5000.00,
"feesWrittenOff": 0.00
}
}
}

Implementation Checklist

Phase 1: Validation & Calculation

  • Implement ValidateWriteOffEligibilityAsync with regulatory checks
  • Add days past due calculation
  • Add collection efforts tracking
  • Implement CalculateWriteOffAmountAsync with provisioning analysis
  • Unit tests for validation rules (10+ scenarios)

Phase 2: Write-Off Execution

  • Implement ProcessLoanWriteOffAsync with approval workflow
  • Add schedule write-off logic with state management
  • Add loan account write-off with field tracking
  • Add provisioning updates
  • Integration tests for write-off execution

Phase 3: GL Posting

  • Implement PostWriteOffGLEntriesAsync for all scenarios
  • Handle fully provisioned write-offs
  • Handle under-provisioned write-offs (additional expense)
  • Add off-balance sheet memo entries (NPL register)
  • Unit tests for GL balancing

Phase 4: Recovery Processing

  • Implement ProcessLoanRecoveryAsync for post-write-off recoveries
  • Add recovery GL entries (recovery income)
  • Link recoveries to original write-off transaction
  • Track collection costs
  • Recovery income reporting

Phase 5: API & Reporting

  • Create write-off validation endpoint
  • Create write-off execution endpoint
  • Create recovery transaction endpoint
  • Add NPL write-off register report
  • Add recovery tracking report
  • Regulatory reporting (CBN format)

Phase 6: Testing & Deployment

  • End-to-end tests (5+ scenarios including recovery)
  • Regulatory compliance tests (CBN guidelines)
  • Performance tests
  • User acceptance testing
  • Production deployment with audit trail

Developer Resources

For API implementation details, see:


Document Version: 1.0
Last Updated: December 28, 2025
Related Documents: