# Accounting System Integration Guide

## Overview

This document describes how the Chart of Accounts and accounting system are integrated with the loan management system and all reports throughout the microfinance platform.

## Integration Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    LOAN & EXPENSE TRANSACTIONS               │
│  (Disbursements, Repayments, Fees, Penalties, Expenses)     │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ Django Signals (post_save)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│              INTEGRATION SERVICE                             │
│   - Auto Journal Entry Creation                              │
│   - Account Mapping                                          │
│   - Double-Entry Validation                                  │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ Creates Journal Entries
                       ▼
┌─────────────────────────────────────────────────────────────┐
│            ACCOUNTING SERVICE                                │
│   - Validates Debits = Credits                               │
│   - Posts to General Ledger                                  │
│   - Updates Running Balances                                 │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ General Ledger Entries
                       ▼
┌─────────────────────────────────────────────────────────────┐
│               CHART OF ACCOUNTS                              │
│   Assets | Liabilities | Equity | Income | Expenses          │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ Account Balances & Transactions
                       ▼
┌─────────────────────────────────────────────────────────────┐
│              REPORTS & DASHBOARDS                            │
│   - P&L Statement                                            │
│   - Balance Sheet                                            │
│   - Trial Balance                                            │
│   - SasaPay Reconciliation                                   │
│   - Dashboard Metrics                                        │
└─────────────────────────────────────────────────────────────┘
```

## Automatic Journal Entry Creation

### 1. Loan Disbursement

**Trigger:** Loan status changes to 'active' and has disbursement_date

**Signal Handler:** `accounting/signals.py::create_loan_disbursement_journal_entry`

**Journal Entry:**
```
DR: 11002 - Loan Portfolio (Asset)         KES X,XXX
    CR: 11003 - Cash (Asset)                       KES X,XXX

Reference: LOAN-DISB-{loan_number}
```

**Implementation:** `accounting/services/integration_service.py::create_loan_disbursement_entry()`

### 2. Loan Repayment

**Trigger:** New Repayment record created

**Signal Handler:** `accounting/signals.py::create_repayment_journal_entry`

**Journal Entry:**
```
DR: 11003/11005 - Cash/M-Pesa (Asset)      KES X,XXX
    CR: 11002 - Loan Portfolio (Asset)             KES X,XXX (principal)
    CR: 41001 - Interest Income (Income)           KES X,XXX (interest)
    CR: 41002 - Fee Income (Income)                KES X,XXX (fees)
    CR: 41003 - Penalty Income (Income)            KES X,XXX (penalty)

Reference: LOAN-REPAY-{receipt_number}
```

**Implementation:** `accounting/services/integration_service.py::create_loan_repayment_entry()`

**Allocation Logic:**
- Repayment amount is automatically split between principal, interest, fees, and penalties
- Uses `repayment.principal_portion`, `repayment.interest_portion`, `repayment.fee_portion`, `repayment.penalty_portion`
- Payment source determines cash account:
  - `automatic` → M-Pesa account (11005)
  - Other → Cash account (11003)

### 3. Processing Fee (Standalone)

**Trigger:** Manual call or loan approval

**Journal Entry:**
```
DR: 11003 - Cash (Asset)                   KES X,XXX
    CR: 41002 - Fee Income (Income)                KES X,XXX

Reference: LOAN-FEE-{loan_number}
```

**Implementation:** `accounting/services/integration_service.py::create_processing_fee_entry()`

**Note:** Usually processing fees are included in repayment allocation, but this method is available for separate fee collection.

### 4. Interest Accrual (Accrual Basis Accounting)

**Trigger:** Manual call or scheduled task

**Journal Entry:**
```
DR: 11006 - Accrued Interest Receivable (Asset)  KES X,XXX
    CR: 41001 - Interest Income (Income)                 KES X,XXX

Reference: INT-ACCR-{loan_number}-{date}
```

**Implementation:** `accounting/services/integration_service.py::create_interest_accrual_entry()`

**Usage:** For accrual basis accounting, call this method daily/monthly to recognize interest income as it's earned, not when cash is received.

### 5. Expense Recording

**Trigger:** Expense status changes to 'approved'

**Signal Handler:** `accounting/signals.py::create_expense_journal_entry`

**Journal Entry:**
```
DR: 5XXXX - Expense Account (Expense)      KES X,XXX
    CR: 11003/11004/11005 - Payment Account       KES X,XXX

Reference: EXP-{expense_id}
```

**Implementation:** `accounting/services/integration_service.py::create_expense_entry()`

**Category Mapping:**
| Expense Category | Account Code | Account Name |
|-----------------|--------------|--------------|
| salaries | 51001 | Salaries & Wages |
| rent | 52001 | Rent Expense |
| utilities | 52002 | Utilities |
| supplies | 52003 | Office Supplies |
| marketing | 53001 | Marketing & Advertising |
| transport | 53002 | Transport & Travel |
| maintenance | 53003 | Maintenance & Repairs |
| communication | 52002 | Utilities |
| professional_fees | 53004 | Professional Fees |
| other | 59001 | Other Operating Expenses |

**Payment Method Mapping:**
| Payment Method | Account Code | Account Name |
|---------------|--------------|--------------|
| cash | 11003 | Cash |
| bank_transfer | 11004 | Bank Accounts |
| cheque | 11004 | Bank Accounts |
| mpesa | 11005 | M-Pesa |
| card | 11004 | Bank Accounts |

## Chart of Accounts Structure

### Assets (Account Type: asset)

#### Current Assets
- **11003** - Cash
- **11004** - Bank Accounts
- **11005** - M-Pesa

#### Loan Portfolio
- **11002** - Loan Portfolio (Gross outstanding principal)
- **11006** - Accrued Interest Receivable

#### Fixed Assets
- **12001** - Office Equipment
- **12002** - Furniture & Fixtures
- **12003** - Computer Equipment

### Liabilities (Account Type: liability)

#### Current Liabilities
- **21001** - Client Savings Accounts
- **21002** - Borrowings

### Equity (Account Type: equity)

- **31001** - Owner's Equity
- **31002** - Retained Earnings

### Income (Account Type: income)

- **41001** - Interest Income
- **41002** - Fee Income (Processing Fees)
- **41003** - Penalty Income
- **41004** - Registration Fee Income

### Expenses (Account Type: expense)

#### Staff Expenses
- **51001** - Salaries & Wages
- **51002** - Employee Benefits

#### Premises Expenses
- **52001** - Rent Expense
- **52002** - Utilities
- **52003** - Office Supplies

#### Operating Expenses
- **53001** - Marketing & Advertising
- **53002** - Transport & Travel
- **53003** - Maintenance & Repairs
- **53004** - Professional Fees

#### Other Expenses
- **59001** - Other Operating Expenses

## Reports Integration

### 1. Profit & Loss Statement

**Location:** `reports/financial_reports_service.py::get_profit_and_loss()`

**Integration Strategy:** Dual-mode operation

**Mode 1: Accounting System (Preferred)**
- Pulls data from General Ledger entries
- Uses `accounting/services/report_service.py::generate_income_statement()`
- Provides accurate accrual-basis income and expenses
- Source: Posted journal entries

**Mode 2: Direct Calculation (Fallback)**
- Calculates from Loan and Expense models directly
- Cash-basis approximation
- Used when accounting system has no data or encounters errors

**How It Works:**
1. Checks if accounting system has data (`GeneralLedger.objects.exists()`)
2. If yes, pulls income from GL accounts:
   - 41001 (Interest Income)
   - 41002 (Fee Income)
   - 41003 (Penalty Income)
3. Pulls expenses from GL expense accounts (51XXX, 52XXX, 53XXX, 59XXX)
4. Calculates Net Profit = Total Income - Total Expenses
5. If accounting fails, falls back to direct loan/expense aggregation

**Report Fields:**
```python
{
    'period': {'start': start_date, 'end': end_date},
    'source': 'accounting_system' or 'direct_calculation',
    'income': {
        'interest_income': Decimal,
        'processing_fee_income': Decimal,
        'registration_fee_income': Decimal,
        'penalty_income': Decimal,
        'total': Decimal,
    },
    'expenses': {
        'by_category': [{'category': str, 'total': Decimal}],
        'total_operational': Decimal,
    },
    'net_profit': Decimal,
    'cash_flow': {
        'loans_disbursed': Decimal,
        'repayments_collected': Decimal,
        'net_cash': Decimal,
    },
}
```

### 2. Balance Sheet (Financial Statements)

**Location:** `reports/financial_reports_service.py::get_financial_statements()`

**Integration Strategy:** Dual-mode operation

**Mode 1: Accounting System (Preferred)**
- Pulls account balances from General Ledger as of specific date
- Uses `accounting/services/accounting_service.py::calculate_account_balance()`
- Assets from accounts: 11002, 11003, 11004, 11005, 11006
- Liabilities from accounts: 21001, 21002
- Equity = Assets - Liabilities

**Mode 2: Direct Calculation (Fallback)**
- Calculates from active/paid/defaulted loans
- Estimates portfolio value and collections
- Used when accounting data unavailable

**Account Balance Retrieval:**
```python
def _get_account_balance(accounting_service, account_code, as_of_date, branch_id):
    """
    Returns: Decimal balance from GL or Decimal('0.00')
    """
```

**Report Fields:**
```python
{
    'as_of': date,
    'source': 'accounting_system' or 'direct_calculation',
    'assets': {
        'current_assets': {
            'cash': Decimal,      # 11003
            'bank': Decimal,      # 11004
            'mpesa': Decimal,     # 11005
            'total': Decimal,
        },
        'loan_portfolio': {
            'gross': Decimal,                # 11002
            'net': Decimal,                  # 11002 - NPL
            'accrued_interest': Decimal,     # 11006
        },
        'total_assets': Decimal,
    },
    'liabilities': {
        'client_savings': Decimal,        # 21001
        'borrowings': Decimal,            # 21002
        'total_liabilities': Decimal,
    },
    'equity': {
        'retained_earnings': Decimal,
        'total_equity': Decimal,
    },
    'npl_ratio': Decimal,
}
```

### 3. Trial Balance

**Location:** `accounting/services/report_service.py::generate_trial_balance()`

**Integration:** Direct from General Ledger

**How It Works:**
1. Retrieves all active accounts from Chart of Accounts
2. Calculates debit/credit balance for each account up to as_of_date
3. Verifies accounting equation: Total Debits = Total Credits
4. Groups accounts by type (Asset, Liability, Equity, Income, Expense)

**Report Fields:**
```python
{
    'report_date': date,
    'branch': Branch or None,
    'accounts': [
        {
            'code': str,
            'name': str,
            'account_type': str,
            'debit_balance': Decimal,
            'credit_balance': Decimal,
        }
    ],
    'totals': {
        'total_debits': Decimal,
        'total_credits': Decimal,
        'difference': Decimal,
    },
    'balanced': bool,
    'by_type': {
        'asset': {'debit': Decimal, 'credit': Decimal},
        'liability': {'debit': Decimal, 'credit': Decimal},
        'equity': {'debit': Decimal, 'credit': Decimal},
        'income': {'debit': Decimal, 'credit': Decimal},
        'expense': {'debit': Decimal, 'credit': Decimal},
    }
}
```

### 4. SasaPay Reconciliation

**Location:** `reports/financial_reports_service.py::get_sasapay_reconciliation()`

**Integration:** Payment system reconciliation with GL control account

**Components:**
- **Money In (C2B):** Repayments via SasaPay IPN → M-Pesa account (11005)
- **Money Out (B2C):** Disbursements via SasaPay → Cash/Bank reduction
- **Control Account Balance:** Net position tracking
- **IPN Status Breakdown:** processed, no_match, duplicate, failed, pending
- **Unknown Payments:** Unmatched transactions requiring investigation

**Future Enhancement:** Cross-reference with GL M-Pesa account (11005) balance for complete reconciliation

### 5. Dashboard Reports

**Location:** `reports/simple_reports_service.py`

**Integration Points:**

1. **Processing Fees Report**
   - Currently: Direct from Loan.processing_fee aggregation
   - Enhanced: Can pull from GL account 41002 when available

2. **Interest Income Report**
   - Currently: Direct from Loan.interest_amount aggregation
   - Enhanced: Can pull from GL account 41001 when available

3. **Portfolio Overview**
   - Loan Portfolio value → GL account 11002 when available
   - Outstanding amounts → GL balance calculations

4. **Collection Rate**
   - Cash collected → Can be verified against GL M-Pesa (11005) and Cash (11003) increases

5. **Expense Tracking**
   - Operational expenses → GL expense accounts (5XXXX)

## Verification & Testing

### Check Integration Status

```python
# Check if accounting system has data
from accounting.models import JournalEntry, GeneralLedger

has_accounting_data = GeneralLedger.objects.exists()
print(f"Accounting system active: {has_accounting_data}")

# Check if signals are working
from loans.models import Loan

recent_loans = Loan.objects.filter(status='active').order_by('-disbursement_date')[:5]
for loan in recent_loans:
    entries = JournalEntry.objects.filter(loan=loan)
    print(f"Loan {loan.loan_number}: {entries.count()} journal entries")
```

### Test Journal Entry Creation

```python
from accounting.services.integration_service import IntegrationService
from loans.models import Loan
from django.contrib.auth import get_user_model

User = get_user_model()
integration_service = IntegrationService()

# Test disbursement entry
loan = Loan.objects.filter(status='active').first()
user = User.objects.filter(role='admin').first()

try:
    entry = integration_service.create_loan_disbursement_entry(loan, user)
    print(f"✓ Created entry: {entry.reference_number}")
except Exception as e:
    print(f"✗ Failed: {e}")
```

### Verify Report Data Source

```python
from reports.financial_reports_service import get_profit_and_loss
from datetime import date, timedelta

end_date = date.today()
start_date = end_date - timedelta(days=30)

pl_report = get_profit_and_loss(start_date, end_date)
print(f"Data source: {pl_report.get('source', 'unknown')}")
print(f"Total income: {pl_report['income']['total']}")
print(f"Net profit: {pl_report['net_profit']}")
```

## Migration Strategy

### Phase 1: Parallel Operation (Current)
- Accounting system runs alongside existing reports
- Reports use dual-mode: try accounting first, fallback to direct calculation
- All new transactions automatically create journal entries
- Historical data remains in loan/expense models only

### Phase 2: Historical Data Migration (Optional)
Create journal entries for all historical transactions:

```python
from accounting.services.integration_service import IntegrationService
from loans.models import Loan, Repayment
from django.contrib.auth import get_user_model

User = get_user_model()
integration_service = IntegrationService()
admin_user = User.objects.filter(role='admin').first()

# Migrate historical disbursements
for loan in Loan.objects.filter(status__in=['active', 'paid', 'completed']):
    if not JournalEntry.objects.filter(
        reference_number=f'LOAN-DISB-{loan.loan_number}'
    ).exists():
        try:
            integration_service.create_loan_disbursement_entry(loan, admin_user)
            print(f"✓ Migrated disbursement: {loan.loan_number}")
        except Exception as e:
            print(f"✗ Failed {loan.loan_number}: {e}")

# Migrate historical repayments
for repayment in Repayment.objects.all():
    if not JournalEntry.objects.filter(
        reference_number=f'LOAN-REPAY-{repayment.receipt_number}'
    ).exists():
        try:
            integration_service.create_loan_repayment_entry(repayment, admin_user)
            print(f"✓ Migrated repayment: {repayment.receipt_number}")
        except Exception as e:
            print(f"✗ Failed {repayment.receipt_number}: {e}")
```

### Phase 3: Full Accounting Mode
- Reports exclusively use accounting system
- Direct calculation mode removed or used only for validation
- All historical data migrated

## Troubleshooting

### Issue: Journal entries not being created

**Check:**
1. Verify signals are registered: Look for `import accounting.signals` in `accounting/apps.py::ready()`
2. Check signal handlers: Logs should show "Created ... journal entry" messages
3. Verify accounts exist: All system accounts (11002, 11003, 41001, etc.) must be active

**Fix:**
```python
# Ensure accounting app is in INSTALLED_APPS
INSTALLED_APPS = [
    ...
    'accounting',
    ...
]

# Check accounts
from accounting.models import Account
required_accounts = ['11002', '11003', '11005', '41001', '41002', '41003']
for code in required_accounts:
    if not Account.objects.filter(code=code, is_active=True).exists():
        print(f"⚠️  Missing account: {code}")
```

### Issue: Debits don't equal credits error

**Cause:** Repayment allocation doesn't sum to repayment amount

**Fix:**
Ensure repayment has correct portions:
```python
repayment.principal_portion + repayment.interest_portion + 
repayment.fee_portion + repayment.penalty_portion == repayment.amount
```

### Issue: Reports show different numbers

**Cause:** Accounting vs. direct calculation discrepancies

**Debug:**
```python
# Compare both sources
pl_accounting = get_profit_and_loss(start, end)  # Should use accounting
print(f"Accounting: {pl_accounting['income']['total']}")

# Force direct calculation by temporarily renaming GeneralLedger
# (Not recommended in production)
```

## Future Enhancements

### 1. Automated Interest Accrual
Schedule daily task to accrue interest on all active loans:
```python
# In accounting/tasks.py
@shared_task
def daily_interest_accrual():
    from accounting.services.integration_service import IntegrationService
    from loans.models import Loan
    
    integration_service = IntegrationService()
    for loan in Loan.objects.filter(status='active'):
        # Calculate daily interest
        daily_interest = loan.interest_amount / loan.duration_days
        integration_service.create_interest_accrual_entry(
            loan, daily_interest, timezone.now().date(), system_user
        )
```

### 2. Bank Reconciliation Module
- Import bank statements
- Match against GL cash/bank accounts (11003, 11004)
- Flag unmatched transactions

### 3. Budget vs. Actual Reporting
- Create budget entries by expense category
- Compare actual expenses (from GL) to budgeted amounts
- Variance analysis

### 4. Multi-Currency Support
- Add currency field to accounts and journal entries
- Exchange rate tracking
- Currency conversion in reports

### 5. Audit Trail Enhancement
- Track all changes to posted journal entries
- Reversal history with reasons
- User action logging

## Support & Documentation

- **Main README:** `accounting/README.md` - Complete accounting module documentation
- **Integration Service:** `accounting/services/integration_service.py` - Auto-entry creation logic
- **Signal Handlers:** `accounting/signals.py` - Trigger definitions
- **Report Service:** `accounting/services/report_service.py` - Financial report generation
- **Financial Reports:** `reports/financial_reports_service.py` - Dashboard reports with GL integration

---

**Last Updated:** January 2025  
**Version:** 1.0.0  
**Maintained By:** Microfinance Development Team
