Generalledger Documentation

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:

Primary Functions

Related Controllers

---

๐Ÿ—„๏ธ Database Tables

Journal Entry Tables

Table NamePurposeKey Columns
**dailyentry**Journal entry headersid, entryComment, thedate, related, reverseofid
**dailyentrydebtor**Debit entriesid, dailyentryid, accountstreeid, value, dComment
**dailyentrycreditor**Credit entriesid, dailyentryid, accountstreeid, value, dComment
### Chart of Accounts

Table NamePurposeKey Columns
**accountstree**Chart of accountsid, name, customName, parent, itemtype
### Reference Tables

Table NamePurposeKey Columns
**youtubelink**Tutorial linksyoutubelinkid, 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:

   $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

---

## ๐Ÿงฎ 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
";

$childIds = getChilds($accountsTreeId);

echo "Child IDs: $childIds
";

$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
";

### 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) . "
";

echo "Total Credits: " . count($TShapeArrCerditor) . "
";

echo "Real Count: $realCount
";

### 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
";

echo "Memory used: " . (($memory_end - $memory_start) / 1024 / 1024) . " MB
";

### 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 "

$description

";

echo "

Query: $query
";

if (!empty($params)) {

echo "

Params: " . print_r($params, true) . "
";

}

$start = microtime(true);

$result = R::getAll($query, $params);

$duration = microtime(true) - $start;

echo "

Results: " . count($result) . " rows in {$duration}s

";

return $result;

}

```

---

๐Ÿ“š Related Documentation

---

Documented By: AI Assistant

Review Status: โœ… Complete

Performance Notes: Complex queries may need optimization for large datasets

Next Review: When major changes occur