# Accounting System - Production Deployment Guide

## 📋 Overview

This guide covers deploying the Haven Grazuri Accounting System to production. The system implements:
- ✅ Double-entry bookkeeping
- ✅ Chart of Accounts (30+ microfinance accounts)
- ✅ General Ledger posting with running balances
- ✅ Journal Entry management
- ✅ Automatic integration with loan and expense systems
- ✅ Multi-branch accounting support
- ✅ Fiscal period management

## 🚀 Deployment Methods

### Method 1: Using Python Deployment Script (Recommended)

```bash
# Full deployment
python deploy_accounting_system.py

# Skip tests (faster)
python deploy_accounting_system.py --skip-tests

# Production mode
python deploy_accounting_system.py --production
```

This script will:
1. ✓ Check Python version
2. ✓ Verify accounting app structure
3. ✓ Run database migrations
4. ✓ Create Chart of Accounts (30+ accounts)
5. ✓ Create fiscal periods (monthly, quarterly, annual)
6. ✓ Verify integrations with loan/expense systems
7. ✓ Run tests
8. ✓ Collect static files

### Method 2: Using Bash Script (Linux/cPanel)

```bash
# Make script executable
chmod +x deploy_accounting_to_cpanel.sh

# Run deployment
./deploy_accounting_to_cpanel.sh
```

### Method 3: Manual Step-by-Step Deployment

#### Step 1: Run Migrations

```bash
python manage.py makemigrations accounting
python manage.py migrate accounting
python manage.py migrate
```

#### Step 2: Add Accounting App to INSTALLED_APPS

Edit `branch_system/settings.py`:

```python
INSTALLED_APPS = [
    # ... existing apps ...
    'accounting.apps.AccountingConfig',  # Add this
]
```

#### Step 3: Create Chart of Accounts

```bash
python manage.py shell
```

Then run:

```python
from accounting.models import Account
from users.models import CustomUser

system_user = CustomUser.objects.filter(role='admin').first()

# Asset accounts
Account.objects.get_or_create(
    code='202001',
    defaults={
        'name': 'Loan Portfolio - Principal',
        'account_type': 'asset',
        'subtype': 'loan_portfolio',
        'description': 'Outstanding principal balance of all active loans',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

Account.objects.get_or_create(
    code='202002',
    defaults={
        'name': 'Accrued Interest Receivable',
        'account_type': 'asset',
        'subtype': 'current_asset',
        'description': 'Interest earned but not yet collected',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

Account.objects.get_or_create(
    code='202003',
    defaults={
        'name': 'Cash on Hand',
        'account_type': 'asset',
        'subtype': 'current_asset',
        'description': 'Physical cash in branches',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

Account.objects.get_or_create(
    code='202004',
    defaults={
        'name': 'Bank Account - Main',
        'account_type': 'asset',
        'subtype': 'current_asset',
        'description': 'Main operating bank account',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

Account.objects.get_or_create(
    code='202005',
    defaults={
        'name': 'M-Pesa Account',
        'account_type': 'asset',
        'subtype': 'current_asset',
        'description': 'M-Pesa business account balance',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

# Income accounts
Account.objects.get_or_create(
    code='401001',
    defaults={
        'name': 'Interest Income - Loans',
        'account_type': 'income',
        'subtype': 'interest_income',
        'description': 'Interest earned from loan products',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

Account.objects.get_or_create(
    code='401002',
    defaults={
        'name': 'Fee Income - Processing Fees',
        'account_type': 'income',
        'subtype': 'fee_income',
        'description': 'Loan processing and application fees',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

# Expense accounts
Account.objects.get_or_create(
    code='501001',
    defaults={
        'name': 'Salaries and Wages',
        'account_type': 'expense',
        'subtype': 'staff_costs',
        'description': 'Employee salaries and wages',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

# Equity accounts
Account.objects.get_or_create(
    code='320001',
    defaults={
        'name': 'Share Capital',
        'account_type': 'equity',
        'subtype': None,
        'description': 'Owner investment in the institution',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

Account.objects.get_or_create(
    code='320002',
    defaults={
        'name': 'Retained Earnings',
        'account_type': 'equity',
        'subtype': None,
        'description': 'Accumulated profits retained in the business',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

# Liability accounts
Account.objects.get_or_create(
    code='302001',
    defaults={
        'name': 'Client Savings Deposits',
        'account_type': 'liability',
        'subtype': 'client_savings',
        'description': 'Savings deposits from microfinance clients',
        'is_system_account': True,
        'is_active': True,
        'created_by': system_user
    }
)

print("Chart of Accounts created successfully!")
exit()
```

#### Step 4: Create Fiscal Periods

```bash
python manage.py shell
```

Then run:

```python
from accounting.models import FiscalPeriod
from datetime import datetime, timedelta

year = datetime.now().year

# Create monthly periods for current year
for month in range(1, 13):
    start_date = datetime(year, month, 1).date()
    if month == 12:
        end_date = datetime(year, 12, 31).date()
    else:
        end_date = (datetime(year, month + 1, 1) - timedelta(days=1)).date()
    
    FiscalPeriod.objects.get_or_create(
        start_date=start_date,
        end_date=end_date,
        defaults={
            'name': start_date.strftime('%B %Y'),
            'period_type': 'monthly',
            'status': 'open'
        }
    )

# Create annual period
FiscalPeriod.objects.get_or_create(
    name=f'FY {year}',
    period_type='annual',
    defaults={
        'start_date': datetime(year, 1, 1).date(),
        'end_date': datetime(year, 12, 31).date(),
        'status': 'open'
    }
)

print("Fiscal periods created successfully!")
exit()
```

#### Step 5: Collect Static Files

```bash
python manage.py collectstatic --noinput
```

#### Step 6: Restart Application

For cPanel/Apache:
```bash
touch tmp/restart.txt
```

For standalone Django:
```bash
# Restart your Django process/service
```

## 📊 Chart of Accounts Structure

### Assets (202xxx)
- `202001` - Loan Portfolio - Principal
- `202002` - Accrued Interest Receivable
- `202003` - Cash on Hand
- `202004` - Bank Account - Main
- `202005` - M-Pesa Account
- `202006` - Allowance for Loan Losses
- `202007` - Office Equipment
- `202008` - Accumulated Depreciation - Equipment

### Liabilities (302xxx)
- `302001` - Client Savings Deposits
- `302002` - Loan Payable - Bank
- `302003` - Accrued Expenses Payable
- `302004` - Interest Payable

### Equity (320xxx)
- `320001` - Share Capital
- `320002` - Retained Earnings
- `320003` - Current Year Earnings

### Income (401xxx)
- `401001` - Interest Income - Loans
- `401002` - Fee Income - Processing Fees
- `401003` - Fee Income - Late Payment Penalties
- `401004` - Other Income

### Expenses (501xxx)
- `501001` - Salaries and Wages
- `501002` - Rent Expense
- `501003` - Utilities Expense
- `501004` - Marketing and Advertising
- `501005` - Office Supplies
- `501006` - Transportation Expense
- `501007` - Loan Loss Provision Expense
- `501008` - Depreciation Expense
- `501009` - Bank Charges
- `501010` - Professional Fees

## ✅ Post-Deployment Verification

### 1. Check Database Tables

```bash
python manage.py shell
```

```python
from accounting.models import Account, JournalEntry, FiscalPeriod
print(f"Accounts: {Account.objects.count()}")
print(f"Journal Entries: {JournalEntry.objects.count()}")
print(f"Fiscal Periods: {FiscalPeriod.objects.count()}")
```

### 2. Test Signal Handlers

Create a test loan and verify journal entry is created:

```python
from loans.models import Loan
from accounting.models import JournalEntry

# Get initial count
initial_count = JournalEntry.objects.count()

# Create/activate a loan (this should trigger signal)
# ... your loan creation code ...

# Check if journal entry was created
new_count = JournalEntry.objects.count()
print(f"Journal entries created: {new_count - initial_count}")
```

### 3. Access Accounting URLs

- Dashboard: `/accounting/dashboard/`
- Chart of Accounts: `/accounting/accounts/`
- Journal Entries: `/accounting/journal-entries/`
- Reports: `/accounting/reports/`

## 🔧 Troubleshooting

### Issue: Migrations fail with "table already exists"

**Solution:**
```bash
python manage.py migrate accounting --fake
```

### Issue: Signal handlers not working

**Solution:**
Check that `accounting.apps.AccountingConfig` is in `INSTALLED_APPS`:

```python
# settings.py
INSTALLED_APPS = [
    # ...
    'accounting.apps.AccountingConfig',  # Must use this format
]
```

### Issue: Chart of Accounts not appearing

**Solution:**
Verify accounts were created:
```bash
python manage.py shell -c "from accounting.models import Account; print(Account.objects.count())"
```

### Issue: Journal entries not posting to General Ledger

**Solution:**
Check fiscal periods are open:
```python
from accounting.models import FiscalPeriod
print(FiscalPeriod.objects.filter(status='open'))
```

## 🔐 Security Checklist

- [ ] Update user permissions for accounting module
- [ ] Restrict access to journal entry creation
- [ ] Limit who can close fiscal periods
- [ ] Enable audit logging for all accounting transactions
- [ ] Setup approval workflow for journal entries
- [ ] Regular database backups

## 📈 Next Steps After Deployment

1. **Configure User Permissions**
   - Assign "View Reports" permission to managers
   - Assign "Create Transactions" to accountants
   - Assign "Close Periods" to CFO/Admin only

2. **Setup Branch-Specific Accounts**
   - Create cash accounts for each branch
   - Create bank accounts for each branch

3. **Test Integration**
   - Disburse a test loan → Verify journal entry
   - Receive a test repayment → Verify journal entry
   - Approve a test expense → Verify journal entry

4. **Run Trial Balance**
   - Generate trial balance report
   - Verify debits = credits

5. **Configure Reports**
   - Setup report templates
   - Configure report periods
   - Test PDF exports

## 📞 Support

For issues or questions:
- Check system logs: `logs/accounting.log`
- Review Django admin: `/admin/accounting/`
- Contact: havenin2023@gmail.com

---

**Haven Grazuri Investment Limited**
*Professional Microfinance Accounting System*
