ExpensesType Documentation

Expenses Type Controller Documentation

File: /controllers/expensesTypeController.php

Purpose: Manages hierarchical expense category system with tree structure, accounting integration, and user group permissions

Last Updated: December 20, 2024

Total Functions: 12+

Lines of Code: ~620

---

๐Ÿ“‹ Overview

The Expenses Type Controller manages a comprehensive expense categorization system with hierarchical tree structure and accounting chart integration. It provides:

Primary Functions

Related Controllers

---

๐Ÿ—„๏ธ Database Tables

Primary Tables (Direct Operations)

Table NamePurposeKey Columns
**expensestype**Expense category hierarchyexpensestypeid, expensestypename, parent, treeId, type, treeType, saveid, addOnlyGroupIds
**expenses**Expense transactionsexpensestypeid, expensesValue, expensesdate, conditions
### Integration Tables

Table NamePurposeKey Columns
**accountstree**Chart of accountsid, name, parent, customName, itemtype
**save**Cash registers/safessaveid, savename, treeId
**usergroup**User permission groupsusergroupid, usergroupname
### Reference Tables

Table NamePurposeKey Columns
**user**System usersuserid, username, usergroupid
**youtubelink**Tutorial linksyoutubelinkid, title, url
---

๐Ÿ”‘ Key Functions

1. Default Action - Add Form Display

Location: Line 96

Purpose: Display hierarchical expense type creation form

Process Flow:

1. Build tree-ordered expense type hierarchy

2. Configure save account access based on permissions

3. Load user groups for permission assignment

4. Display form with tree structure

Tree Building:

orderExtepensesTypeParentsAsTree(0); 
$allParents = $dataOrdered;
$smarty->assign("allParents", $allParents);

---

2. add() - Create Expense Type

Location: Line 289

Purpose: Create new expense type with accounting integration

Function Signature:

function add()

Process Flow:

1. Input Processing:

   $name = $_POST['name'];
   $withinsupervision_ratio = $_POST['withinsupervision_ratio'];
   $supervision_ratiotype = $_POST['supervision_ratiotype'];
   $supervision_amount = $_POST['supervision_amount'];
   $parentid = $_POST['parent'];
   $type = (int) $_POST["type"];
   $treeType = (int) $_POST["treeType"];
   $saveid = (int) $_POST["saveid"];
   ```

2. **User Group Processing**:
   ```php
   $addOnlyGroupIds = filter_input(INPUT_POST, 'addOnlyGroupIds', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
   $addOnlyGroupIds_str = implode(',', $addOnlyGroupIds);
   ```

3. **Database Insert**:
   ```php
   $id = $expensesTypeDAO->insert($expensesType);
   ```

4. **Chart of Accounts Integration**:
   ```php
   if ($parentid < 1) {
       if ($treeType == 0) $parent = 414;      // Operating expenses
       elseif ($treeType == 1) $parent = 413;  // Administrative expenses  
       elseif ($treeType == 2) $parent = 412;  // Cost of goods sold
   } else {
       $parentData = $expensesTypeDAO->load($parentid);
       $parent = $parentData->treeId;
   }
   $treeId = addTreeElement($name, $parent, 3, 0, 0, '', 0, 0);
   ```

**Tree Type Categories**:
- `treeType = 0` - Operating Expenses (Tree ID 414)
- `treeType = 1` - Administrative Expenses (Tree ID 413)  
- `treeType = 2` - Cost of Goods Sold (Tree ID 412)

---

### 3. **show()** - Display Expense Types
**Location**: Line 351  
**Purpose**: Show hierarchical expense type listing with filtering

**Process Flow**:
1. Build tree-ordered hierarchy for display
2. Apply parent filter if specified
3. Process name-based filtering with tree traversal

**Hierarchy Display**:
php

orderExtepensesTypeParentsAsTree(0);

$allData = $dataOrdered;

$name = $_REQUEST['parent'];

if ($name != '-1') {

$name = ltrim(str_replace("_", " ", $name));

$qname = $expensesTypeExt->queryAllname($name);

} else {

$qname = $allData;

}

---

### 4. **orderExtepensesTypeParentsAsTree()** - Build Tree Structure
**Location**: Line 258  
**Purpose**: Recursively build hierarchical tree display with indentation

**Function Signature**:
php

function orderExtepensesTypeParentsAsTree($parent, $expensestypeid = 0, $level)

**Process Flow**:
1. **Permission-Based Filtering**:
   ```php
   $queryString = '';
   if ($_SESSION['searchinonesave'] == 0) {
       if ($_SESSION['saveids'] != 0) {
           $queryString = ' and (expensestype.saveid = 0 or expensestype.saveid in (' . $_SESSION['saveids'] . '))';
       }
   } else {
       $queryString = ' and expensestype.saveid = ' . $_SESSION['saveid'];
   }
   ```

2. **Recursive Tree Building**:
   ```php
   $result = $expensesTypeExt->getTypesWithoutExpenses(" and expensestype.expensestypeid != $expensestypeid and expensestype.parent = $parent $queryString");
   
   foreach ($result as $type) {
       $type->conditions = $level;
       $preString = str_repeat('_', $level);
       $type->expensestypename = $preString . ' ' . $type->expensestypename;
       array_push($dataOrdered, $type);
       orderExtepensesTypeParentsAsTree($type->expensestypeid, $expensestypeid, $level + 1);
   }
   ```

**Visual Hierarchy Example**:

Operating Expenses

_ Utilities

__ Electricity

__ Water

_ Transportation

__ Fuel

__ Maintenance

Administrative Expenses

_ Office Supplies

_ Professional Services

---

### 5. **update()** - Modify Expense Type
**Location**: Line 425  
**Purpose**: Update expense type with accounting tree synchronization

**Process Flow**:
1. Update expense type record
2. Synchronize with chart of accounts
3. Handle parent relationship changes

**Tree Synchronization**:
php

$oldData = $expensesTypeDAO->load($id);

$treeId = $oldData->treeId;

$getRow = $accountsTreeDAO->load($treeId);

if ($parentid == 0) {

$getRow->parent = 151; // Default parent

} else {

$oldData2 = $expensesTypeDAO->load($parentid);

$getRow->parent = $oldData2->treeId;

}

editTreeElement($getRow);

---

### 6. **executeOperation()** - Bulk Operations
**Location**: Line 375  
**Purpose**: Perform bulk operations on selected expense types

**Process Flow**:
1. Parse selected expense type IDs
2. Execute operation (currently supports delete)
3. Check dependencies for each item
4. Generate operation report

**Bulk Delete Implementation**:
php

$choosedItemArr = $_POST['choosedItem'];

foreach ($choosedItemArr as $expensesTypeId) {

if ($operationType == '1') { // delete

$note = deleteExt($expensesTypeId);

if ($note != "success") {

$expenseTypeData = $expensesTypeDAO->load($expensesTypeId);

$outputString .= $expenseTypeData->expensestypename . ": " . $note . "
";

}

}

}

---

### 7. **delete()** - Delete Expense Type with Validation
**Location**: Line 532  
**Purpose**: Delete expense type with dependency checking

**Process Flow**:
1. **Dependency Validation**:
   ```php
   // Check for associated expenses
   $expensesdata = $expensesRecord->queryByExpensestypeid($expensestypeid);
   
   // Check for child categories
   $childCategories = $expensesTypeDAO->queryByParent($expensestypeid);
   
   if (count($expensesdata) > 0 || count($childCategories) > 0) {
       $note = "ู„ุง ูŠู…ูƒู† ุญุฐู ู‡ุฐุง ุงู„ู†ูˆุน ู„ุฃู†ู‡ ู…ุฑุชุจุท ุจุจูŠุงู†ุงุช ุฃุฎุฑู‰";
   }
   ```

2. **Safe Deletion**:
   ```php
   $rowDelData = $expensesTypeDAO->load($expensestypeid);
   delTreeElement($rowDelData->expensestypename);
   
   $expensesType->conditions = 1; // Mark as deleted
   $expensesTypeExt->updateConditions($expensesType);
   ```

---

## ๐Ÿ”„ Workflows

### Workflow 1: Hierarchical Expense Type Creation

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ START: Create Expense Type โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 1. Load Form Dependencies โ”‚

โ”‚ - Build parent hierarchy tree โ”‚

โ”‚ - Load user groups for permissions โ”‚

โ”‚ - Configure save account access โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 2. Process Form Input โ”‚

โ”‚ - Validate expense type name โ”‚

โ”‚ - Set supervision configurations โ”‚

โ”‚ - Process user group permissions โ”‚

โ”‚ - Determine tree type and parent โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 3. Create Database Record โ”‚

โ”‚ - Insert expense type record โ”‚

โ”‚ - Generate unique expense type ID โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 4. Chart of Accounts Integration โ”‚

โ”‚ IF No Parent (parentid = 0): โ”‚

โ”‚ โ”œโ”€ Use default tree parent by type โ”‚

โ”‚ โ”‚ โ”œโ”€ Operating: 414 โ”‚

โ”‚ โ”‚ โ”œโ”€ Administrative: 413 โ”‚

โ”‚ โ”‚ โ””โ”€ COGS: 412 โ”‚

โ”‚ ELSE: โ”‚

โ”‚ โ””โ”€ Use parent's tree ID โ”‚

โ”‚ โ”‚

โ”‚ - Create tree element in chart โ”‚

โ”‚ - Link expense type to tree ID โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 5. Finalize Creation โ”‚

โ”‚ - Update expense type with tree ID โ”‚

โ”‚ - Return success response โ”‚

โ”‚ - Redirect to success page โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

---

### Workflow 2: Tree Structure Display Building

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ START: Build Tree Structure โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 1. Initialize Tree Building โ”‚

โ”‚ - Set parent = 0 (root level) โ”‚

โ”‚ - Clear ordered data array โ”‚

โ”‚ - Set level = 0 โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 2. Build Permission Filter โ”‚

โ”‚ IF searchinonesave = 0: โ”‚

โ”‚ IF saveids != 0: โ”‚

โ”‚ โ””โ”€ Filter: saveid = 0 OR saveid in (saveids) โ”‚

โ”‚ ELSE: โ”‚

โ”‚ โ””โ”€ Filter: saveid = current saveid โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 3. Recursive Processing โ”‚

โ”‚ FOR EACH Level: โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ”œโ”€ Query children of current parent โ”‚

โ”‚ โ”‚ โ””โ”€ Apply permission filters โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ”œโ”€ Process each child: โ”‚

โ”‚ โ”‚ โ”œโ”€ Set hierarchy level โ”‚

โ”‚ โ”‚ โ”œโ”€ Add indentation prefix โ”‚

โ”‚ โ”‚ โ”‚ โ””โ”€ preString = repeat('_', level) โ”‚

โ”‚ โ”‚ โ”œโ”€ Update display name โ”‚

โ”‚ โ”‚ โ””โ”€ Add to ordered array โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ””โ”€ Recurse for each child: โ”‚

โ”‚ โ””โ”€ Call with (childId, level+1) โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 4. Return Hierarchical Structure โ”‚

โ”‚ - Complete tree with visual indentation โ”‚

โ”‚ - Proper parent-child ordering โ”‚

โ”‚ - Permission-filtered results โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

---

## ๐ŸŒ URL Routes & Actions

| URL Parameter | Function Called | Description |
|---------------|----------------|-------------|
| `do=` (empty) | Default action | Display expense type creation form |
| `do=add` | `add()` | Create new expense type |
| `do=show` | `show()` | Display expense type hierarchy |
| `do=executeOperation` | `executeOperation()` | Bulk operations on selected types |
| `do=edit` | `edit()` | Load expense type for editing |
| `do=update` | `update()` | Update existing expense type |
| `do=delete` | `delete()` | Delete expense type with validation |

### Required Parameters by Action

**Add Expense Type** (`do=add`):
- `name` - Expense type name
- `descripe` - Description (optional)
- `parent` - Parent expense type ID (0 for root)
- `type` - Type classification
- `treeType` - Chart of accounts category (0/1/2)
- `saveid` - Associated save account ID
- `addOnlyGroupIds[]` - User group permissions array

**Show with Filter** (`do=show`):
- `parent` - Parent name filter (optional, "-1" for all)

**Bulk Operations** (`do=executeOperation`):
- `operation` - Operation type ("1" for delete)
- `choosedItem[]` - Array of expense type IDs

**Edit/Update** (`do=edit`, `do=update`):
- `id` - Expense type ID
- Additional parameters same as add for update

---

## ๐Ÿงฎ Calculation Methods

### Tree Path Generation
php

function orderExtepensesTypeParentsAsTree($parent, $expensestypeid = 0, $level) {

// Generate visual hierarchy

$preString = '';

for ($index = 0; $index < $level; $index++) {

$preString .= '_';

}

$type->expensestypename = $preString . ' ' . $type->expensestypename;

}

### Permission Group Processing
php

$addOnlyGroupIds = filter_input(INPUT_POST, 'addOnlyGroupIds', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

$addOnlyGroupIds_str = '';

foreach ($addOnlyGroupIds as $value) {

$addOnlyGroupIds_str .= $value . ",";

}

$addOnlyGroupIds_str = rtrim($addOnlyGroupIds_str, ',');

### Chart of Accounts Parent Assignment
php

if ($parentid < 1) {

if ($treeType == 0) $parent = 414; // Operating expenses

elseif ($treeType == 1) $parent = 413; // Administrative expenses

elseif ($treeType == 2) $parent = 412; // Cost of goods sold

} else {

$parentData = $expensesTypeDAO->load($parentid);

$parent = $parentData->treeId;

}

---

## ๐Ÿ”’ Security & Permissions

### User Authentication
php

// Standard authentication check

include_once("../public/authentication.php");

### Save Account Access Control
php

if ($_SESSION['searchinonesave'] == 0) {

if ($_SESSION['saveids'] == 0) {

$saves = $saveDAO->queryAll(); // Access to all

} else {

// Restricted to specific save accounts

$queryString = ' and saveid in (' . $_SESSION['saveids'] . ')';

$saves = $saveEX->queryWithConditionWithQueryString($queryString);

}

} else {

// Single save account mode

$saves = $_SESSION['saveid'];

}

### JSON API Security
php

if (isset($_POST['curlpost']) && $_POST['curlpost'] == 1) {

// API response format

$data = array(

'status' => 1,

'message' => 'ุชู…ุช ุงู„ุนู…ู„ูŠู‡ ุจู†ุฌุงุญ',

'message_en' => 'Success',

'id' => $id

);

echo json_encode($data);

} else {

// Web interface redirect

header("location:?do=sucess");

}

### Input Sanitization
- All POST data filtered and validated
- SQL injection prevention via DAO layer
- User group array validation
- Parent-child relationship validation
- Dependency checking before deletion

---

## ๐Ÿ“Š Performance Considerations

### Database Optimization Tips
1. **Indexes Required**:
   - `expensestype(parent, conditions)` - For tree traversal
   - `expensestype(saveid)` - For permission filtering
   - `expensestype(expensestypename)` - For name searches
   - `expenses(expensestypeid)` - For dependency checking

2. **Query Optimization**:
   - Recursive tree building can be expensive for deep hierarchies
   - Consider caching tree structure for frequently accessed data
   - Use efficient parent-child queries

3. **Memory Management**:
   - Tree building stores all nodes in memory
   - Consider pagination for very large hierarchies
   - Efficient permission filtering at query level

### Known Performance Issues
sql

-- This recursive query can be slow for deep hierarchies

SELECT * FROM expensestype

WHERE parent = ? AND conditions = 0

ORDER BY expensestypename;

-- Solution: Add composite index

CREATE INDEX idx_parent_conditions ON expensestype(parent, conditions, expensestypename);

---

## ๐Ÿ› Common Issues & Troubleshooting

### 1. **Tree Hierarchy Display Problems**
**Issue**: Missing indentation or incorrect ordering  
**Cause**: Recursive function logic errors or missing parent relationships

**Debug**:
php

// Add debugging to tree building

echo "Processing parent: $parent, level: $level
";

print_r($result);

### 2. **Chart of Accounts Integration Errors**
**Issue**: Missing tree IDs or broken account links  
**Cause**: Failed tree element creation or invalid parent IDs

**Debug**:
sql

SELECT et.expensestypeid, et.expensestypename, et.treeId,

at.name as account_name, at.parent

FROM expensestype et

LEFT JOIN accountstree at ON et.treeId = at.id

WHERE et.treeId IS NULL OR at.id IS NULL;

### 3. **Permission Filtering Issues**
**Issue**: Users seeing expense types they shouldn't access  
**Cause**: Incorrect save account filtering or permission logic

**Debug**:
php

// Check user permissions

echo "Search in one save: " . $_SESSION['searchinonesave'] . "
";

echo "Save IDs: " . $_SESSION['saveids'] . "
";

echo "Current save ID: " . $_SESSION['saveid'] . "
";

### 4. **Bulk Operation Failures**
**Issue**: Incomplete bulk deletions or error reporting  
**Cause**: Dependency validation errors or transaction issues

**Debug**:
php

// Enhanced error reporting

foreach ($choosedItemArr as $expensesTypeId) {

try {

$note = deleteExt($expensesTypeId);

echo "ID $expensesTypeId: $note
";

} catch (Exception $e) {

echo "Error deleting $expensesTypeId: " . $e->getMessage() . "
";

}

}

---

## ๐Ÿงช Testing Scenarios

### Test Case 1: Hierarchical Structure Creation

1. Create root level expense type

2. Create child expense types under root

3. Create grandchild types under children

4. Verify tree display shows proper indentation

5. Check chart of accounts integration

### Test Case 2: Permission-Based Filtering

1. Login with restricted save account access

2. Create expense types with different save assignments

3. Verify only permitted types appear

4. Test bulk operations with mixed permissions

### Test Case 3: Dependency Validation

1. Create expense type

2. Create expenses using the type

3. Attempt to delete the type

4. Verify deletion is prevented

5. Test error message display

### Debug Mode Enable
php

// Add at top of controller for debugging

error_reporting(E_ALL);

ini_set('display_errors', 1);

// Debug tree building

function debug_tree_building($parent, $level) {

echo str_repeat(" ", $level) . "Processing parent: $parent at level: $level
";

}

```

---

๐Ÿ“š Related Documentation

---

Documented By: AI Assistant

Review Status: โœ… Complete

Next Review: When major changes occur