# General Ledger Controller Documentation

**File**: `/controllers/generalledger.php`  
**Purpose**: Generates detailed general ledger reports with journal entry analysis and T-account formatting for accounting review  
**Last Updated**: December 20, 2024  
**Total Functions**: 8+  
**Lines of Code**: ~559

---

## 📋 Overview

The General Ledger Controller is a sophisticated accounting reporting module that generates detailed general ledger reports with journal entry analysis. It provides:
- Account-specific general ledger reports with date filtering
- Journal entry analysis with debit/credit breakdown
- T-account format display with traditional accounting layout
- Hierarchical account processing with child account consolidation
- Complex journal entry relationship mapping
- Multiple display formats (standard list vs T-account format)
- Account tree traversal for comprehensive reporting
- Advanced filtering and sorting capabilities

### Primary Functions
- [x] Generate account-specific general ledger reports
- [x] Journal entry analysis with debit/credit classification
- [x] T-account format display for traditional accounting view
- [x] Hierarchical account tree processing
- [x] Date range filtering with flexible formats
- [x] Complex entry relationship mapping
- [x] Multi-format report output options
- [x] Account balance calculation and tracking

### Related Controllers
- [accountstree.md](accountstree.md) - Chart of accounts management
- [dailyentry.md](dailyentry.md) - Journal entry management
- [assistantledger.md](assistantledger.md) - Subsidiary ledger reports

---

## 🗄️ Database Tables

### Journal Entry Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **dailyentry** | Journal entry headers | id, entryComment, thedate, related, reverseofid |
| **dailyentrydebtor** | Debit entries | id, dailyentryid, accountstreeid, value, dComment |
| **dailyentrycreditor** | Credit entries | id, dailyentryid, accountstreeid, value, dComment |

### Chart of Accounts
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **accountstree** | Chart of accounts | id, name, customName, parent, itemtype |

### Reference Tables
| Table Name | Purpose | Key Columns |
|------------|---------|-------------|
| **youtubelink** | Tutorial links | youtubelinkid, title, url |

---

## 🔑 Key Functions

### 1. **Default Action** - Report Interface
**Location**: Line 76  
**Purpose**: Display general ledger report interface with account selection

**Process Flow**:
1. **Parameter Processing**:
   ```php
   $accountsTreeId = filter_input(INPUT_POST, "generalledgerid");
   $datefrom = filter_input(INPUT_POST, "datefrom");
   $dateto = filter_input(INPUT_POST, "dateto");
   $shape = filter_input(INPUT_POST, "shape");
   ```

2. **Default Values**:
   ```php
   if (empty($accountsTreeId) && empty($datefrom) && empty($dateto)) {
       $datefrom = date('Y-m-d');
       $dateto = date('Y-m-d');
   }
   
   if (empty($accountsTreeId)) {
       $accountsTreeId = -1;
   }
   
   if (empty($shape) || !isset($shape)) {
       $shape = 0;
   }
   ```

3. **Account Processing**:
   ```php
   $childData = $accountsTreeDAO->queryByParent($accountsTreeId);
   if (count($childData) > 0 && $type->itemtype2 == 0) {
       $cildrenIds = getChilds($accountsTreeId);
       $cildrenIds = rtrim($cildrenIds, ",");
       DrawTableByAccountNew($cildrenIds, $datefrom, $dateto, $shape, $accountsTreeId);
   }
   ```

---

### 2. **DrawTableByAccountNew()** - Advanced Ledger Generation
**Location**: Line 129  
**Purpose**: Generate comprehensive general ledger report with complex entry relationships

**Function Signature**:
```php
function DrawTableByAccountNew($id, $startDate, $endDate, $shape, $accountsTreeId)
```

**Process Flow**:
1. **Date Filter Setup**:
   ```php
   $queryString_date = '';
   if (isset($startDate) && !empty($startDate)) {
       $queryString_date .= ' and dailyentry.thedate >= "' . $startDate . '" ';
   }
   if (isset($endDate) && !empty($endDate)) {
       $queryString_date .= ' and dailyentry.thedate <= "' . $endDate . '" ';
   }
   ```

2. **Query String Construction**:
   ```php
   $queryStringCreditor = ' where dailyentrycreditor.accountstreeid in( ' . $id . ') ' . $queryString_date . ' ';
   $queryStringDebtor = ' where dailyentrydebtor.accountstreeid in( ' . $id . ') ' . $queryString_date . ' ';
   ```

3. **Entry ID Collection**:
   ```php
   $dailyEntryIds = R::getAll('
       (SELECT dailyentry.id
        FROM dailyentry
        JOIN dailyentrydebtor ON dailyentrydebtor.dailyentryid = dailyentry.id
        ' . $queryStringDebtor . ')
       UNION
       (SELECT dailyentry.id
        FROM dailyentry  
        JOIN dailyentrycreditor ON dailyentrycreditor.dailyentryid = dailyentry.id
        ' . $queryStringCreditor . ')
   ');
   
   $IDS = '0,';
   foreach ($dailyEntryIds as $value) {
       $IDS .= $value['id'] . ",";
   }
   $IDS = rtrim($IDS, ",");
   ```

4. **Debit Entry Processing**:
   ```php
   $mainItem = R::getAll('
       SELECT dailyentry.id, dailyentrydebtor.value, 0 as type
       FROM dailyentry
       JOIN dailyentrydebtor ON dailyentrydebtor.dailyentryid = dailyentry.id
       ' . $queryString . ' AND accountstreeid in (' . $id . ')
       ORDER BY dailyentry.related, dailyentry.id, dailyentrydebtor.id
   ');
   ```

5. **Related Account Processing**:
   ```php
   $resultDataArr = R::getAll('
       SELECT dailyentry.*, 
              dailyentrycreditor.id as idChild,
              dailyentrycreditor.accountstreeid,
              dailyentrycreditor.value,
              dailyentrycreditor.dComment,
              0 as type,
              CONCAT(dailyentrycreditor.accountstreeid,"-",parent.customName,"/",accountstree.customName) as accountstreeName
       FROM dailyentry
       JOIN dailyentrycreditor ON dailyentrycreditor.dailyentryid = dailyentry.id
       JOIN accountstree ON accountstree.id = dailyentrycreditor.accountstreeid
       JOIN accountstree parent ON parent.id = accountstree.parent
       AND accountstree.id not in( ' . $id . ') 
       AND dailyentry.id in( ' . $IDS . ' )
       ORDER BY dailyentry.related, dailyentry.id, dailyentrycreditor.id
   ');
   ```

6. **Entry Relationship Analysis**:
   ```php
   foreach ($mainItem as $myItem) {
       $itemBroCount = count($itemBro[$myItem['id']]) + 1;
       $result = $resultDataArr[$myItem['id']];
       $resultCount = count($result);
       
       if ($itemBroCount == 1 && $resultCount >= 1) {
           // Single account transaction
           foreach ($result as $value) {
               if ($shape == 1) {
                   array_push($TShapeArrDebtor, $value);
               } else {
                   array_push($allDailyEntery, $value);
               }
           }
       } elseif ($itemBroCount > 1 && $resultCount == 1) {
           // Multiple debit accounts, single credit
           $result[0]->value = $myItem['value'];
           if ($shape == 1) {
               array_push($TShapeArrDebtor, $result[0]);
           } else {
               array_push($allDailyEntery, $result[0]);
           }
       } elseif ($itemBroCount > 1 && $resultCount > 1) {
           // Compound entry - multiple accounts on both sides
           $result[0]->accountstreeName = "مذكورين";
           $result[0]->value = $myItem['value'];
           if ($shape == 1) {
               array_push($TShapeArrCerditor, $result[0]);
           } else {
               array_push($allDailyEntery, $result[0]);
           }
       }
   }
   ```

7. **T-Account Format Processing**:
   ```php
   if ($shape == 1) {
       $dailyEntry = new Dailyentry();
       $debtorItr = 0;
       $creditorItr = 0;
       $realCount = 0;
       $totalCount = count($TShapeArrDebtor) + count($TShapeArrCerditor);
       
       for ($i = 0; $i < ($totalCount * 2); $i++) {
           if ($i % 2 == 0) {
               // Debit side
               if (empty($TShapeArrDebtor[$debtorItr])) {
                   array_push($allDailyEntery, $dailyEntry);
               } else {
                   array_push($allDailyEntery, $TShapeArrDebtor[$debtorItr]);
                   $debtorItr++;
               }
           } else {
               // Credit side
               if (empty($TShapeArrCerditor[$creditorItr])) {
                   array_push($allDailyEntery, $dailyEntry);
               } else {
                   array_push($allDailyEntery, $TShapeArrCerditor[$creditorItr]);
                   $creditorItr++;
               }
           }
       }
   }
   ```

---

### 3. **getChilds()** - Recursive Account Tree Traversal
**Location**: Line 545  
**Purpose**: Recursively collect all child account IDs for consolidated reporting

**Function Signature**:
```php
function getChilds($parent)
```

**Implementation**:
```php
function getChilds($parent) {
    global $accountsTreeEX;
    global $cildrenIds;
    
    $result = $accountsTreeEX->queryByParentExt($parent);
    if (count($result) > 0) {
        foreach ($result as $type) {
            $cildrenIds .= $type->id . ',';
            getChilds($type->id);
        }
    }
    return $cildrenIds;
}
```

---

### 4. **sortById()** - Entry Sorting
**Location**: Line 520  
**Purpose**: Sort journal entries by ID with configurable order

**Function Signature**:
```php
function sortById($type)
```

**Implementation**:
```php
function sortById($type) {
    global $allDailyEntery;
    
    $membresCount = count($allDailyEntery) - 1;
    foreach ($allDailyEntery as $myalloutRole) {
        for ($i = 0; $i < $membresCount; $i++) {
            if ($type == "desc") {
                if ($allDailyEntery[$i]->id < $allDailyEntery[$i + 1]->id) {
                    $tempMember = $allDailyEntery[$i];
                    $allDailyEntery[$i] = $allDailyEntery[$i + 1];
                    $allDailyEntery[$i + 1] = $tempMember;
                }
            } elseif ($type == "asc") {
                if ($allDailyEntery[$i]->id > $allDailyEntery[$i + 1]->id) {
                    $tempMember = $allDailyEntery[$i + 1];
                    $allDailyEntery[$i + 1] = $allDailyEntery[$i];
                    $allDailyEntery[$i] = $tempMember;
                }
            }
        }
    }
}
```

---

## 🔄 Workflows

### Workflow 1: General Ledger Report Generation
```
┌─────────────────────────────────────────────────────────────┐
│              START: General Ledger Request                  │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Parameter Processing                                    │
│     - Extract account ID (generalledgerid)                 │
│     - Extract date range (datefrom, dateto)                │
│     - Extract display format (shape)                       │
│     - Apply default values if empty                        │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Account Hierarchy Processing                            │
│     IF Account has children:                                │
│       ├─ Call getChilds() recursively                      │
│       ├─ Build comma-separated child ID list               │
│       └─ Include all child accounts in report              │
│     ELSE:                                                   │
│       └─ Process single account only                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Entry ID Collection                                     │
│     A. Debit Entry Query:                                   │
│       ├─ JOIN dailyentry with dailyentrydebtor            │
│       ├─ Filter by account IDs and date range             │
│       └─ Collect all matching entry IDs                    │
│     B. Credit Entry Query:                                  │
│       ├─ JOIN dailyentry with dailyentrycreditor          │
│       ├─ Filter by account IDs and date range             │
│       └─ Collect all matching entry IDs                    │
│     C. UNION Results and Build ID List                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Debit Entry Analysis                                    │
│     FOR EACH Entry in Target Account(s):                   │
│       │                                                     │
│       ├─ Get main debit item details                       │
│       ├─ Count related debit accounts (brothers)           │
│       ├─ Get corresponding credit accounts                 │
│       ├─ Build account name with parent hierarchy          │
│       │                                                     │
│       └─ Classify Entry Type:                              │
│           ├─ Simple (1 debit, 1+ credit)                  │
│           ├─ Multiple debit, single credit                │
│           └─ Compound (multiple debit, multiple credit)    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. Credit Entry Analysis                                   │
│     FOR EACH Entry in Target Account(s):                   │
│       │                                                     │
│       ├─ Get main credit item details                      │
│       ├─ Count related credit accounts (brothers)          │
│       ├─ Get corresponding debit accounts                  │
│       ├─ Build account name with parent hierarchy          │
│       │                                                     │
│       └─ Classify Entry Type:                              │
│           ├─ Simple (1 credit, 1+ debit)                  │
│           ├─ Multiple credit, single debit                │
│           └─ Compound (multiple credit, multiple debit)    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  6. Display Format Selection                                │
│     IF Standard Format (shape = 0):                        │
│       ├─ Sort entries by ID ascending                      │
│       ├─ Display chronological listing                     │
│       └─ Show account names and amounts                    │
│     ELIF T-Account Format (shape = 1):                     │
│       ├─ Separate debit and credit arrays                 │
│       ├─ Alternate between debit/credit sides             │
│       ├─ Fill empty spaces for alignment                   │
│       └─ Display traditional T-account layout              │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  7. Template Assignment and Display                         │
│     - Assign processed entry array to template             │
│     - Include account information and date range           │
│     - Display general ledger report view                   │
│     - Include tutorial links if available                  │
└─────────────────────────────────────────────────────────────┘
```

---

### Workflow 2: T-Account Format Processing
```
┌─────────────────────────────────────────────────────────────┐
│              START: T-Account Layout                        │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Separate Debit and Credit Arrays                       │
│     - TShapeArrDebtor: All debit entries                   │
│     - TShapeArrCerditor: All credit entries                │
│     - Calculate total count for layout                      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Alternating Layout Generation                           │
│     FOR i = 0 to (totalCount * 2):                        │
│       │                                                     │
│       IF i is even (i % 2 == 0):                          │
│         ├─ Process debit side                              │
│         ├─ Add debit entry or empty space                  │
│         └─ Increment debit iterator                        │
│       ELSE:                                                │
│         ├─ Process credit side                             │
│         ├─ Add credit entry or empty space                 │
│         └─ Increment credit iterator                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. T-Account Display Result                               │
│     ┌─────────────────┬─────────────────┐                  │
│     │   DEBIT SIDE    │   CREDIT SIDE   │                  │
│     ├─────────────────┼─────────────────┤                  │
│     │ Entry 1         │ Entry 1         │                  │
│     │ Entry 2         │ (empty)         │                  │
│     │ (empty)         │ Entry 2         │                  │
│     │ Entry 3         │ Entry 3         │                  │
│     └─────────────────┴─────────────────┘                  │
└─────────────────────────────────────────────────────────────┘
```

---

## 🌐 URL Routes & Actions

| URL Parameter | Function Called | Description |
|---------------|----------------|-------------|
| `do=` (empty) | Default action | Display general ledger interface and process reports |
| `do=sucess` | Success page | Display success message |
| `do=error` | Error page | Display error message |

### Required Parameters
**Generate General Ledger** (POST to default action):
- `generalledgerid` - Account tree ID (-1 for all accounts)
- `datefrom` - Start date (YYYY-MM-DD)
- `dateto` - End date (YYYY-MM-DD)
- `shape` - Display format (0=standard, 1=T-account)

### Form Example
```html
<form method="post" action="generalledger.php">
    <select name="generalledgerid">
        <option value="-1">All Accounts</option>
        <option value="100">Cash Account</option>
        <option value="200">Accounts Receivable</option>
    </select>
    
    <input type="date" name="datefrom" value="2024-01-01">
    <input type="date" name="dateto" value="2024-01-31">
    
    <select name="shape">
        <option value="0">Standard Format</option>
        <option value="1">T-Account Format</option>
    </select>
    
    <input type="submit" value="Generate Report">
</form>
```

---

## 🧮 Calculation Methods

### Account Hierarchy ID Collection
```php
function getChilds($parent) {
    global $accountsTreeEX;
    global $cildrenIds;
    
    $result = $accountsTreeEX->queryByParentExt($parent);
    if (count($result) > 0) {
        foreach ($result as $type) {
            $cildrenIds .= $type->id . ',';
            getChilds($type->id);  // Recursive call
        }
    }
    return $cildrenIds;
}
```

### Entry Classification Logic
```php
// Classification based on number of related accounts
if ($itemBroCount == 1 && $resultCount >= 1) {
    // Simple entry: 1 debit account, 1+ credit accounts
    $entryType = 'simple';
} elseif ($itemBroCount > 1 && $resultCount == 1) {
    // Multiple debits to single credit
    $entryType = 'multiple_debit';
} elseif ($itemBroCount > 1 && $resultCount > 1) {
    // Compound entry: multiple debits and credits
    $entryType = 'compound';
    $accountName = "مذكورين"; // Arabic for "mentioned" (multiple accounts)
}
```

### T-Account Layout Algorithm
```php
$totalCount = count($TShapeArrDebtor) + count($TShapeArrCerditor);

for ($i = 0; $i < ($totalCount * 2); $i++) {
    if ($i % 2 == 0) {
        // Even indices = debit side
        if (!empty($TShapeArrDebtor[$debtorItr])) {
            array_push($allDailyEntery, $TShapeArrDebtor[$debtorItr]);
            $debtorItr++;
        } else {
            array_push($allDailyEntery, new Dailyentry()); // Empty space
        }
    } else {
        // Odd indices = credit side  
        if (!empty($TShapeArrCerditor[$creditorItr])) {
            array_push($allDailyEntery, $TShapeArrCerditor[$creditorItr]);
            $creditorItr++;
        } else {
            array_push($allDailyEntery, new Dailyentry()); // Empty space
        }
    }
}
```

---

## 🔒 Security & Permissions

### Authentication Check
```php
include_once("../public/authentication.php");
```

### Input Validation
```php
$accountsTreeId = filter_input(INPUT_POST, "generalledgerid");
$datefrom = filter_input(INPUT_POST, "datefrom");
$dateto = filter_input(INPUT_POST, "dateto");
$shape = filter_input(INPUT_POST, "shape");

// Basic validation
if (empty($shape) || !isset($shape)) {
    $shape = 0;
}
```

### SQL Injection Prevention
- Uses RedBeanPHP ORM with parameterized queries
- Input filtered through `filter_input()` functions
- Account IDs validated through DAO layer
- No direct SQL string concatenation in critical paths

---

## 📊 Performance Considerations

### Database Optimization Tips
1. **Critical Indexes Required**:
   ```sql
   CREATE INDEX idx_dailyentry_date ON dailyentry(thedate);
   CREATE INDEX idx_dailyentry_related ON dailyentry(related, id);
   CREATE INDEX idx_debtordaily_account ON dailyentrydebtor(accountstreeid, dailyentryid);
   CREATE INDEX idx_creditordaily_account ON dailyentrycreditor(accountstreeid, dailyentryid);
   CREATE INDEX idx_accountstree_parent ON accountstree(parent);
   ```

2. **Query Optimization Issues**:
   ```sql
   -- This UNION query can be expensive for large date ranges
   SELECT dailyentry.id FROM dailyentry
   JOIN dailyentrydebtor ON dailyentrydebtor.dailyentryid = dailyentry.id
   WHERE dailyentrydebtor.accountstreeid in (account_list)
   AND dailyentry.thedate BETWEEN 'start' AND 'end'
   
   UNION
   
   SELECT dailyentry.id FROM dailyentry  
   JOIN dailyentrycreditor ON dailyentrycreditor.dailyentryid = dailyentry.id
   WHERE dailyentrycreditor.accountstreeid in (account_list)
   AND dailyentry.thedate BETWEEN 'start' AND 'end';
   ```

3. **Performance Bottlenecks**:
   - Recursive `getChilds()` function for deep account hierarchies
   - Complex JOIN queries with account tree traversal
   - Multiple database hits for entry relationship analysis
   - Large result set processing in memory

### Optimization Recommendations
```php
// Cache account hierarchy
function getCachedChildren($parentId) {
    static $cache = array();
    if (!isset($cache[$parentId])) {
        $cache[$parentId] = getChilds($parentId);
    }
    return $cache[$parentId];
}

// Optimize entry ID collection
function getEntryIdsBatch($accountIds, $startDate, $endDate) {
    $placeholders = str_repeat('?,', count($accountIds) - 1) . '?';
    $params = array_merge($accountIds, [$startDate, $endDate, $startDate, $endDate]);
    
    return R::getCol("
        SELECT DISTINCT dailyentry.id FROM dailyentry
        WHERE dailyentry.id IN (
            SELECT dailyentryid FROM dailyentrydebtor WHERE accountstreeid IN ($placeholders)
            UNION
            SELECT dailyentryid FROM dailyentrycreditor WHERE accountstreeid IN ($placeholders)
        )
        AND dailyentry.thedate BETWEEN ? AND ?
        ORDER BY dailyentry.id
    ", $params);
}
```

---

## 🐛 Common Issues & Troubleshooting

### 1. **Missing Entries in Report**
**Issue**: Expected journal entries not appearing  
**Cause**: Account hierarchy not properly traversed

**Debug**:
```php
echo "Account ID: $accountsTreeId<br>";
$childIds = getChilds($accountsTreeId);
echo "Child IDs: $childIds<br>";

$entryCount = R::getCell("
    SELECT COUNT(*) FROM dailyentry de
    JOIN dailyentrydebtor dd ON de.id = dd.dailyentryid
    WHERE dd.accountstreeid IN ($childIds)
    AND de.thedate BETWEEN ? AND ?
", [$startDate, $endDate]);
echo "Entry count: $entryCount<br>";
```

### 2. **T-Account Format Misalignment**
**Issue**: Debit/credit sides not properly aligned  
**Cause**: Incorrect array indexing or empty entry handling

**Debug**:
```php
echo "Total Debits: " . count($TShapeArrDebtor) . "<br>";
echo "Total Credits: " . count($TShapeArrCerditor) . "<br>";
echo "Real Count: $realCount<br>";
```

### 3. **Performance Issues with Large Date Ranges**
**Issue**: Timeout or memory errors for extensive periods  
**Cause**: Large result sets and complex processing

**Monitor**:
```php
$start_time = microtime(true);
$memory_start = memory_get_usage();

// ... processing code ...

$end_time = microtime(true);
$memory_end = memory_get_usage();

echo "Execution time: " . ($end_time - $start_time) . " seconds<br>";
echo "Memory used: " . (($memory_end - $memory_start) / 1024 / 1024) . " MB<br>";
```

### 4. **Entry Relationship Errors**
**Issue**: Wrong accounts shown as related to entries  
**Cause**: Complex JOIN logic or data inconsistencies

**Validate**:
```sql
-- Check for orphaned entries
SELECT de.id, de.entryComment 
FROM dailyentry de
WHERE de.id NOT IN (
    SELECT DISTINCT dailyentryid FROM dailyentrydebtor
    UNION 
    SELECT DISTINCT dailyentryid FROM dailyentrycreditor
);

-- Verify debit/credit balance
SELECT de.id, 
       (SELECT SUM(value) FROM dailyentrydebtor WHERE dailyentryid = de.id) as debits,
       (SELECT SUM(value) FROM dailyentrycreditor WHERE dailyentryid = de.id) as credits
FROM dailyentry de
HAVING debits != credits;
```

---

## 🧪 Testing Scenarios

### Test Case 1: Single Account Ledger
```
1. Select specific account with known transactions
2. Set date range covering known entries
3. Verify all entries appear correctly
4. Check debit/credit classification
5. Validate account relationships
```

### Test Case 2: Hierarchical Account Processing
```
1. Select parent account with child accounts
2. Verify child accounts included in report
3. Check recursive tree traversal
4. Validate consolidated results
```

### Test Case 3: T-Account Format
```
1. Generate report with T-account format (shape=1)
2. Verify debit/credit side alignment
3. Check empty space handling
4. Validate traditional accounting layout
```

### Test Case 4: Complex Journal Entries
```
1. Test compound entries (multiple debits/credits)
2. Verify "مذكورين" (mentioned) display
3. Check entry classification logic
4. Validate amount aggregation
```

### Debug Mode Enable
```php
// Add comprehensive debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('memory_limit', '512M');

function debug_query($description, $query, $params = []) {
    echo "<h4>$description</h4>";
    echo "<pre>Query: $query</pre>";
    if (!empty($params)) {
        echo "<pre>Params: " . print_r($params, true) . "</pre>";
    }
    
    $start = microtime(true);
    $result = R::getAll($query, $params);
    $duration = microtime(true) - $start;
    
    echo "<p>Results: " . count($result) . " rows in {$duration}s</p>";
    return $result;
}
```

---

## 📚 Related Documentation

- [CLAUDE.md](/Applications/AMPPS/www/erp19/CLAUDE.md) - PHP 8.2 migration guide
- [accountstree.md](accountstree.md) - Chart of accounts management
- [dailyentry.md](dailyentry.md) - Journal entry management
- [assistantledger.md](assistantledger.md) - Subsidiary ledger reports

---

**Documented By**: AI Assistant  
**Review Status**: ✅ Complete  
**Performance Notes**: Complex queries may need optimization for large datasets  
**Next Review**: When major changes occur