# Expenses Controller Ajax Documentation

**File**: `/controllers/expensesControllerajex.php`  
**Purpose**: Ajax-based expense management with accounting integration, cost center allocation, and third-party API integration  
**Last Updated**: December 20, 2024  
**Total Functions**: 20+  
**Lines of Code**: ~794

---

## 📋 Overview

The Expenses Controller Ajax provides comprehensive expense management functionality with real-time Ajax capabilities. It handles:
- Dynamic expense type management and Ajax search functionality
- Complex accounting journal entry creation for expenses
- Cash register/safe integration with balance validation
- Cost center allocation and tracking
- Multi-level approval and deletion workflows
- Third-party transportation API integration (Africano system)
- Employee and doctor expense tracking
- Advanced search and filtering with user permission controls

### Primary Functions
- [x] Ajax-powered expense type search and management
- [x] Real-time cash register balance validation
- [x] Automatic accounting journal entry generation
- [x] Cost center allocation and expense tracking
- [x] Multi-source expense categorization (assets vs expenses)
- [x] Third-party API integration for transportation expenses
- [x] Employee/doctor expense assignment
- [x] Permission-based expense access control
- [x] Complex expense reversal with accounting cleanup

### Related Controllers
- [expensesController.php](#) - Main expense processing
- [dailyentryfun.php](dailyentryfun.md) - Accounting journal entries
- [saveController.php](#) - Cash register management
- [costcenterController.php](#) - Cost center management

---

## 🗄️ Database Tables

### Core Expense Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **expensestype** | Expense category definitions | expensestypeid, expensestypename, saveid, withinsupervision_ratio |
| **expenses** | Expense transactions | expensesid, expensesname, expensesValue, expensestypeid, dailyentryid |
| **save** | Cash registers/safes | saveid, savename, savecurrentvalue, treeId |
| **savedaily** | Cash register transaction log | savedailyid, saveid, savedailychangeamount, savedailychangetype |

### Accounting Integration Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **dailyentry** | Journal entry headers | id, totalcreditor, totaldebtor, entryComment, operationId |
| **dailyentrycreditor** | Credit side entries | dailyentryid, accountstreeid, value, dComment |
| **dailyentrydebtor** | Debit side entries | dailyentryid, accountstreeid, value, dComment |
| **accountstree** | Chart of accounts | id, name, customName, treeId |

### Cost Center & Allocation Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **costcenter** | Cost center definitions | id, name, description |
| **costcenterdetail** | Cost allocations | costcenterid, costamount, modelid, tablename, type |
| **assetscat** | Asset categories | id, name, description |
| **assets** | Asset master data | assetId, assetsName |
| **employeedoctor** | Employee/doctor records | id, name, thetype |

---

## 🔑 Key Functions

### 1. **add** - Expense Type Creation (Ajax)
**Location**: Line 182  
**Purpose**: Real-time expense type creation during expense entry

**Process Flow**:
1. Validate expense type name and details
2. Create new expensestype record
3. Link to accounts tree for accounting integration
4. Return updated expense type list for dropdown

### 2. **update** - Comprehensive Expense Update
**Location**: Line 427  
**Purpose**: Complex expense modification with accounting reversal and recreation

**Function Logic**:
```php
function update() {
    global $ExpenseDAO, $dailyEntry, $CostcenterdetailEX;
    
    // Get expense data
    $expenseid = $_POST['expenseid'];
    $expensevalue = $_POST['expensevalue'];
    $expensetype = $_POST['expensetype'];
    $dailyentryid = $_POST['dailyentryid'];
    
    $oldData = $ExpenseDAO->load($expenseid);
    
    // Handle condition changes (delete/restore)
    if ($tempdele != $conditions) {
        if ($conditions == 0) { // Restore expense
            // Validate cash register balance
            $saveValueBefore = getSaveValue();
            if ($saveValueBefore >= $expensevalue) {
                // Update cash register
                updateSave($saveId, $saveValuebefore - $expensevalue);
                // Create journal entries
                createExpenseJournalEntry($expensevalue, $expensetype);
            } else {
                return "0"; // Insufficient funds
            }
        } elseif ($conditions == 1) { // Delete expense
            // Reverse cash register transaction
            updateSave($saveId, $saveValuebefore + $expensevalue);
            // Update cost center details
            $CostcenterdetailEX->updatedellbytypeandmodelid('6', $expenseid, 1);
        }
    }
    
    // Reverse old journal entry
    $x = reverseEntryWithItsID($dailyentryid);
    
    // Create new journal entry with updated values
    $returnedData = insertEntery($dailyEntry, $dailyEntryDebtorArray, $dailyEntryCreditorArray);
    $newDailyEntryId = $returnedData[1];
    
    // Update expense record
    $ExpenseDAO->update($updatedExpense);
    
    return "1"; // Success
}
```

### 3. **expensetypesave** - Ajax Expense Type Search
**Location**: Line 253  
**Purpose**: Real-time search for expense types with cash register filtering

**Function Logic**:
```php
function expensetypesave() {
    global $ExpensetypeEX;
    
    $searchTerm = $_POST['searchTerm'];
    $saveid = $_POST['saveid'];
    
    $queryString = " and expensestypename LIKE '%" . $searchTerm . "%' ";
    
    if ($saveid) {
        $queryString .= " and ( saveid = " . $saveid . " or saveid = 0 ) ";
    }
    
    $expensetypes = $ExpensetypeEX->expensetypesave($queryString);
    $return_arr = array();
    
    foreach ($expensetypes as $pro) {
        $row_array = array();
        $row_array['id'] = $pro->expensestypeid;
        $row_array['text'] = $pro->expensestypename;
        array_push($return_arr, $row_array);
    }
    
    echo json_encode($return_arr);
}
```

### 4. **expensetypesaveuser** - User-Restricted Expense Search
**Location**: Line 269  
**Purpose**: Expense type search with user permission filtering

**Permission Logic**:
```php
if ($_SESSION['searchinonesave'] == 0 && $_SESSION['saveids'] != 0) {
    $queryString = ' and saveid in (' . $_SESSION['saveids'] . ') or saveid = 0 ';
}

foreach ($expensetypes as $pro) {
    $row_array = array();
    $row_array['id'] = $pro->expensestypeid;
    $row_array['text'] = $pro->expensestypename;
    $row_array['withinsupervisionratio'] = $pro->withinsupervision_ratio;
    array_push($return_arr, $row_array);
}
```

---

## 🌐 Third-Party API Integration

### Africano Transportation System Integration
**Location**: Lines 290-370  
**Purpose**: Integration with external transportation management system

### 5. **triptype** - Transportation Type Ajax
```php
elseif ($do == "triptype") {
    $return_arr = array();
    $response = CURL_Request(array(), getenv('africanoUrl') . '/trip-type', 1, 0, getenv('africanoToken'));
    $receivedata = json_decode($response);
    
    foreach ($receivedata->data as $data) {
        $row_array = array();
        $row_array['id'] = $data->id;
        $row_array['text'] = $data->name;
        array_push($return_arr, $row_array);
    }
    echo json_encode($return_arr);
}
```

### 6. **trips** - Trip Management Ajax
```php
elseif ($do == "trips") {
    $response = CURL_Request(array(), getenv('africanoUrl') . '/trip', 1, 0, getenv('africanoToken'));
    // Process trip data for dropdown population
}
```

### 7. **dateid** - Trip Date Filtering
```php
elseif ($do == "dateid") {
    $tripid = (int) $_REQUEST['tripid'];
    $response = CURL_Request(array(), getenv('africanoUrl') . '/trip-date?trip_id=' . $tripid, 1, 0, getenv('africanoToken'));
    
    foreach ($receivedata->data as $data) {
        $row_array = array();
        $row_array['id'] = $data->id;
        $row_array['text'] = $data->trip_name . "/" . $data->day_date;
        $row_array['trip_id'] = $data->trip_id;
        array_push($return_arr, $row_array);
    }
}
```

**API Endpoints Integrated**:
- `/trip-type` - Transportation type definitions
- `/trip` - Available trips
- `/trip-date` - Trip scheduling dates
- `/path` - Route definitions
- `/referrer` - Referral sources
- `/driver` - Driver information
- `/bus` - Vehicle information

---

## 🔑 Utility Functions

### 8. **expenseTypeAjax** - Dynamic Expense Type Loading
**Location**: Line 717  
**Purpose**: Load expense types based on user permissions and search criteria

```php
function expenseTypeAjax() {
    global $ExpensetypeEX, $userData;
    
    $name = $_GET['term'];
    $limit = 15;
    
    if ($userData->searchinonesave == 0) {
        $allParents = $ExpensetypeEX->queryAllExtNotParent();
    } else {
        $allParents = $ExpensetypeEX->queryAllWithSaveNotParent($_SESSION['saveid']);
    }
    
    foreach ($allParents as $value) {
        if ($value->parent > 0) {
            $value->expensestypename = "$value->parentexpensestypename / $value->expensestypename";
        }
        
        $row_array['id'] = $value->expensestypeid;
        $row_array['text'] = $value->expensestypename;
        array_push($return_arr, $row_array);
    }
    
    echo json_encode($return_arr);
}
```

### 9. **Cash Register Management Functions**

**getSaveValue()** - Get Current Cash Balance:
```php
function getSaveValue() {
    global $SaveDAO;
    $saveData = $SaveDAO->load($_SESSION['saveid']);
    return $saveData->savecurrentvalue;
}
```

**updateSave()** - Update Cash Register:
```php
function updateSave($saveid, $savevalueafter) {
    global $Save, $SaveExt;
    
    $Save->savecurrentvalue = $savevalueafter;
    $Save->userid = $_SESSION['userid'];
    $Save->saveid = $saveid;
    
    $SaveExt->updateSaveValue($Save);
}
```

**insertSavedaily()** - Log Cash Transactions:
```php
function insertSavedaily($savedailysavebefore, $savedailychangeamount, $savedailychangetype, 
                        $saveid, $processname, $savedailymodelid, $savedailysaveafter, $tablename) {
    global $Savedaily, $SavedailyDAO;
    
    $Savedaily->savedailydate = date("Y-m-d H:i:s");
    $Savedaily->userid = $_SESSION['userid'];
    $Savedaily->savedailysavebefore = $savedailysavebefore;
    $Savedaily->savedailychangeamount = $savedailychangeamount;
    $Savedaily->savedailychangetype = $savedailychangetype;
    $Savedaily->saveid = $saveid;
    $Savedaily->processname = $processname;
    $Savedaily->savedailymodelid = $savedailymodelid;
    $Savedaily->savedailysaveafter = $savedailysaveafter;
    $Savedaily->tablename = $tablename;
    
    $SavedailyDAO->insert($Savedaily);
}
```

---

## 🔄 Workflows

### Workflow 1: Complete Expense Processing with Accounting Integration
```
┌─────────────────────────────────────────────────────────────┐
│              START: Create New Expense                     │
│   User selects expense type and enters amount              │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Ajax Expense Type Search                               │
│     - User types in expense type field                     │
│     - Ajax call to expensetypesaveuser endpoint            │
│     - Apply user permission filters:                       │
│       * Check searchinonesave setting                     │
│       * Filter by assigned save IDs                       │
│       * Include supervision ratio data                     │
│     - Return filtered expense types as JSON               │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Cash Register Balance Validation                       │
│     - Get current cash register balance via getSaveValue() │
│     - Compare available balance vs expense amount          │
│     - If insufficient funds:                               │
│       * Show error message                                 │
│       * Prevent expense creation                           │
│     - If sufficient funds: proceed to next step            │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Accounting Journal Entry Creation                      │
│     A. Prepare Debit Entry (Expense Account):              │
│       - Load expense type to get linked account tree ID    │
│       - Create dailyentrydebtor entry:                     │
│         * accountstreeid = expense type tree ID            │
│         * value = expense amount                           │
│         * dComment = expense description                   │
│                                                             │
│     B. Prepare Credit Entry (Cash Account):                │
│       - Get cash register's linked account tree ID         │
│       - Create dailyentrycreditor entry:                   │
│         * accountstreeid = cash account tree ID            │
│         * value = expense amount                           │
│         * dComment = "Payment for " + expense name         │
│                                                             │
│     C. Create Journal Entry Header:                        │
│       - totalcreditor = totaldedbtor = expense amount      │
│       - entryComment = "Expense: " + expense description   │
│       - userid = current user                              │
│       - thedate = current date                             │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Execute Complete Transaction                           │
│     A. Call insertEntery() from dailyentryfun.php:         │
│       - Validate debit = credit amounts                    │
│       - Insert dailyentry header record                    │
│       - Insert all debit and credit detail records         │
│       - Update account balances in accountstree            │
│       - Return journal entry ID                            │
│                                                             │
│     B. Update Cash Register:                                │
│       - Calculate new balance: current - expense amount    │
│       - Update save.savecurrentvalue                       │
│       - Call insertSavedaily() to log transaction:         │
│         * savedailychangetype = 1 (decrease)               │
│         * processname = "Expense Entry"                    │
│         * link to expense ID for tracking                  │
│                                                             │
│     C. Handle Cost Center Allocation (if applicable):      │
│       - If expense type has cost center assignment         │
│       - Call insertCostCenterDetail():                     │
│         * Link expense amount to specific cost center      │
│         * Enable detailed cost tracking and reporting      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. Create Expense Master Record                           │
│     - Insert into expenses table:                          │
│       * expensesname = user entered description            │
│       * expensesValue = amount                             │
│       * expensestypeid = selected expense type             │
│       * dailyentryid = journal entry ID (for reversal)     │
│       * expensesdate = current date                        │
│       * userid = current user                              │
│       * conditions = 0 (active)                            │
│     - Link expense to accounting system                    │
│     - Enable future editing and reversal                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  6. Finalization & User Feedback                          │
│     - Commit all database transactions                     │
│     - Update UI with success message                       │
│     - Refresh expense list                                 │
│     - Update cash register balance display                 │
│     - Log audit trail for expense creation                 │
│                                                             │
│  ERROR HANDLING:                                            │
│     - Rollback all transactions on any failure             │
│     - Restore original cash register balance               │
│     - Show specific error message to user                  │
│     - Log error details for debugging                      │
└─────────────────────────────────────────────────────────────┘
```

---

### Workflow 2: Expense Update with Accounting Reversal
```
┌─────────────────────────────────────────────────────────────┐
│              START: Edit Existing Expense                  │
│   User modifies expense amount or type                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Load Original Expense Data                             │
│     - Load expense record by ID                            │
│     - Get original: amount, type, journal entry ID         │
│     - Check current conditions (active/deleted)            │
│     - Load linked journal entry details                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Handle Status Changes (Delete/Restore)                 │
│     IF conditions changed (delete ↔ restore):              │
│       A. Delete Expense (conditions = 1):                  │
│         - Reverse cash register transaction                │
│         - Add expense amount back to cash balance          │
│         - Update cost center details (mark deleted)        │
│         - Mark expense as deleted                          │
│                                                             │
│       B. Restore Expense (conditions = 0):                 │
│         - Validate sufficient cash balance                 │
│         - Subtract expense amount from cash               │
│         - Restore cost center allocations                 │
│         - Recreate accounting entries                     │
│                                                             │
│     IF no status change: proceed to amount/type update     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Reverse Original Accounting Entry                      │
│     - Call reverseEntryWithItsID(original_dailyentryid)    │
│     - This creates a complete reversing entry:             │
│       * Original debits → new credits                      │
│       * Original credits → new debits                      │
│       * Same amounts, opposite effects                     │
│       * Updates account balances back to pre-expense state │
│     - Mark original entry as reversed                      │
│     - Original cash and expense account balances restored  │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Create New Accounting Entry with Updated Values        │
│     A. Prepare Updated Debit Entry:                        │
│       - Use NEW expense type (may have changed)            │
│       - Use NEW expense amount                             │
│       - Load expense type's chart of accounts link        │
│                                                             │
│     B. Prepare Updated Credit Entry:                       │
│       - Same cash register account                        │
│       - NEW expense amount                                 │
│                                                             │
│     C. Create New Journal Entry:                           │
│       - Call insertEntery() with updated amounts          │
│       - Get new journal entry ID                          │
│       - Update account balances with new amounts          │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. Update Expense Master Record                           │
│     - Update expenses table with:                          │
│       * New expensesname (description)                     │
│       * New expensesValue (amount)                         │
│       * New expensestypeid (if changed)                    │
│       * New dailyentryid (new journal entry)               │
│       * Updated userid and timestamp                       │
│     - Maintain audit trail of changes                      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  6. Cash Register Adjustment                               │
│     IF expense amount changed:                             │
│       A. Calculate difference: new_amount - old_amount     │
│       B. If difference > 0 (increase):                     │
│         - Validate sufficient cash balance                 │
│         - Decrease cash balance by difference              │
│       C. If difference < 0 (decrease):                     │
│         - Increase cash balance by absolute difference     │
│       D. Log cash register transaction                     │
│                                                             │
│     Update save.savecurrentvalue and log in savedaily      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  7. Return Success Status                                   │
│     - Return appropriate status code:                       │
│       * "1" = Standard update successful                   │
│       * "2" = Delete/restore successful                    │
│       * "0" = Insufficient funds error                     │
│     - UI updates based on return value                     │
│     - Refresh expense list and cash balance display        │
└─────────────────────────────────────────────────────────────┘
```

---

## 🌐 Ajax API Endpoints

| Endpoint | Method | Purpose | Parameters | Response |
|----------|---------|---------|------------|----------|
| `?do=add` | POST | Create new expense type | `expenseName`, `expensecomment` | HTML template |
| `?do=edit` | GET | Load expense for editing | `id` | HTML template |
| `?do=update` | POST | Update expense | `expenseid`, `expensevalue`, `expensetype`, etc. | Status code |
| `?do=isParent` | GET | Check if expense type has children | `id` | "isParent" or "isNotParent" |
| `?do=hasExpenses` | GET | Check if type has expenses | `id` | "yes" or "no" |
| `?do=checkExp` | POST | Validate expense name uniqueness | `expensesname` | -1 or 0 |
| `?do=addCat` | POST | Add expense category | `name`, `descripe`, `parent` | Redirect |
| `?do=expenseTypeAjax` | GET | Get expense types for dropdown | `assetorexpense` | JSON array |
| `?do=getemployeeOrDoctorajax` | GET | Get employee/doctor list | `employeeOrDoctor` | JSON array |
| `?do=expensetypesave` | POST | Search expense types | `searchTerm`, `saveid` | JSON array |
| `?do=expensetypesaveuser` | POST | User-restricted expense search | `searchTerm`, `saveid` | JSON array |

### Third-Party API Endpoints
| Endpoint | Purpose | External API |
|----------|---------|--------------|
| `?do=triptype` | Transportation types | `/trip-type` |
| `?do=trips` | Available trips | `/trip` |
| `?do=paths` | Route definitions | `/path` |
| `?do=dateid` | Trip dates | `/trip-date` |
| `?do=referrer` | Referral sources | `/referrer` |
| `?do=driver` | Driver information | `/driver` |
| `?do=bus` | Vehicle information | `/bus` |

---

## 🔒 Security & Permissions

### User Permission Controls
```php
// Expense type filtering based on user permissions
if ($_SESSION['searchinonesave'] == 0 && $_SESSION['saveids'] != 0) {
    $queryString = ' and saveid in (' . $_SESSION['saveids'] . ') or saveid = 0 ';
}

// User can only see expense types for their assigned cash registers
$userData = $myUserRecord->load($_SESSION['userid']);
if ($userData->searchinonesave == 0) {
    $allParents = $ExpensetypeEX->queryAllExtNotParent();
} else {
    $allParents = $ExpensetypeEX->queryAllWithSaveNotParent($_SESSION['saveid']);
}
```

### Input Validation
```php
// Validate expense amounts
$expensevalue = (float) filter_input(INPUT_POST, 'expensevalue');
if ($expensevalue <= 0) {
    return "0"; // Invalid amount
}

// Validate expense type exists
$expenseTypeData = $ExpensetypeDAO->load($expensetype);
if (empty($expenseTypeData)) {
    return "0"; // Invalid expense type
}
```

### Cash Register Protection
```php
// Prevent negative cash balances
$currentBalance = getSaveValue();
if ($currentBalance < $expenseAmount) {
    return "0"; // Insufficient funds
}
```

---

## 📊 Performance Considerations

### Ajax Optimization
1. **Efficient Search Queries**:
   - Use LIKE queries with proper indexing
   - Limit results to prevent large response sizes
   - Cache frequently accessed expense types

2. **Database Indexes Required**:
   ```sql
   CREATE INDEX idx_expensestype_name ON expensestype(expensestypename, saveid);
   CREATE INDEX idx_expenses_type ON expenses(expensestypeid, conditions);
   CREATE INDEX idx_savedaily_save ON savedaily(saveid, savedailydate);
   ```

### Third-Party API Performance
```php
// Add timeout and error handling for external APIs
$response = CURL_Request(array(), $url, 1, 0, $token, [
    'timeout' => 10,
    'connecttimeout' => 5,
    'retry_count' => 2
]);

if ($response === false) {
    // Return empty array or cached data
    echo json_encode([]);
    exit;
}
```

---

## 🐛 Common Issues & Troubleshooting

### 1. **Ajax Search Not Working**
**Issue**: Expense type search returns no results  
**Cause**: Permission filtering or empty search parameters

**Debug**:
```php
// Log search parameters
error_log("Search term: " . $_POST['searchTerm']);
error_log("Save ID: " . $_POST['saveid']);
error_log("User save restriction: " . $_SESSION['searchinonesave']);
error_log("User save IDs: " . $_SESSION['saveids']);
```

### 2. **Insufficient Funds Error**
**Issue**: Cannot create expense despite having cash  
**Cause**: Cash register balance calculation error

**Debug**:
```php
$currentBalance = getSaveValue();
$expenseAmount = $_POST['expensevalue'];
error_log("Current balance: $currentBalance");
error_log("Expense amount: $expenseAmount");
error_log("Difference: " . ($currentBalance - $expenseAmount));
```

### 3. **Journal Entry Imbalance**
**Issue**: Accounting entries don't balance  
**Cause**: Currency conversion or discount calculation errors

**Debug**:
```php
$totalDebits = getTotal($dailyEntryDebtorArray);
$totalCredits = getTotal($dailyEntryCreditorArray);
error_log("Debits: $totalDebits, Credits: $totalCredits");
```

### 4. **Third-Party API Failures**
**Issue**: External API calls timeout or return errors  
**Cause**: Network issues or API key problems

**Debug**:
```php
error_log("API URL: " . getenv('africanoUrl'));
error_log("API Token: " . substr(getenv('africanoToken'), 0, 10) . "...");
error_log("API Response: " . $response);
```

---

## 🧪 Testing Scenarios

### Test Case 1: Basic Expense Creation
```
1. Select expense type via Ajax search
2. Enter valid expense amount
3. Verify cash register balance check
4. Submit expense form
5. Verify journal entry creation
6. Check cash register balance update
7. Confirm expense appears in expense list
```

### Test Case 2: Expense Update with Reversal
```
1. Edit existing expense
2. Change amount and/or type
3. Verify original journal entry reversal
4. Verify new journal entry creation
5. Check cash register adjustment
6. Confirm updated expense data
```

### Test Case 3: Permission-Based Search
```
1. Login as user with restricted save access
2. Search for expense types
3. Verify only permitted types returned
4. Login as admin user
5. Verify all types available
```

### Test Case 4: Third-Party API Integration
```
1. Test each Africano API endpoint
2. Verify JSON response structure
3. Test with invalid API credentials
4. Test network timeout handling
5. Verify graceful degradation
```

---

## 📚 Related Documentation

- [CLAUDE.md](/Applications/AMPPS/www/erp19/CLAUDE.md) - PHP 8.2 migration guide
- [dailyentryfun.md](dailyentryfun.md) - Accounting journal functions
- [expensesController.php](#) - Main expense processing
- [Ajax Best Practices Guide](#) - Frontend integration patterns

---

**Documented By**: AI Assistant  
**Review Status**: ✅ Complete  
**Next Review**: When expense processing logic changes