📝 Markdown Document Manager

Upload markdown files and organize them in a tree structure

Add Document
Document Tree
Delete Document
View Document

Add New Document


Or Upload Markdown File

Max file size: 2M | Supported: .md, .txt, .markdown files | Content limit: 5MB

Document Tree Structure

🗑️ Delete Document

⚠️ Warning: This action cannot be undone!

EXPENSESTYPE_CONTROLLER_API_DOCUMENTATION

Created: 2025-08-09 08:53:32 | Updated: 2025-08-09 08:53:32

▶ Expenses Type Controller API Documentation

■ 1. Controller Overview

  • Purpose: Comprehensive expense type/category management system with hierarchical organization
  • Module: Financial Management → Expense Types (انواع المصروفات)
  • File: controllers/expensesTypeController.php (620 lines)
  • Primary Database Table: expensestype
  • Dependencies:
  • → Extensive DAO/DTO pattern with MySQL implementations
  • → Daily entry system integration (dailyentryfun.php)
  • → Accounts tree integration for chart of accounts
  • → CURL integration for external systems (initiateStaticSessionCommingWithCurl.php)
  • → Multi-language support (Arabic localization)
  • → Hierarchical tree structure management
  • → User group permissions and save (cash box) management

■ 2. New Version Updates (v2024)

• 🆕 Enhanced CURL/External Integration

  • New File Integration: initiateStaticSessionCommingWithCurl.php (Line 8)
  • CURL Post Support: Enhanced support for external systems calling the controller
  • JSON Response Integration: All operations support JSON responses for API compatibility

// CURL POST detection throughout controller

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

    // JSON response for external systems

    $data = array('status' => 1, 'message' => 'تمت العمليه بنجاح', 'message_en' => 'Success');

    ">echo json_encode($data);
} else {
    // Standard web response

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

• 🆕 Daily Entry Integration

  • Accounting Integration: include_once("dailyentryfun.php") (Line 9)
  • Chart of Accounts: Automatic integration with accounting tree via addTreeElement() and editTreeElement()
  • Account Tree Management: Each expense type creates corresponding account in chart of accounts

• 🆕 Advanced Hierarchical Management

  • Tree Structure: Complex parent-child relationships with unlimited depth
  • Tree Type Classification: Multiple tree types (0, 1, 2) for different accounting classifications
  • Supervision Control: Built-in supervision ratios and approval workflows
  • Save Integration: Expense types can be linked to specific cash boxes/safes

■ 3. Business Logic Analysis

• Core Operations

  1. CREATE: Add new expense types with hierarchical positioning and accounting integration
  2. READ: Display expense types in hierarchical tree structure with filtering
  3. UPDATE: Edit expense type information with tree repositioning
  4. DELETE: Soft delete with comprehensive dependency checking
  5. BULK OPERATIONS: Mass operations on selected expense types
  6. TREE MANAGEMENT: Hierarchical organization with parent-child relationships
  7. ACCOUNTING INTEGRATION: Automatic chart of accounts synchronization
  8. PERMISSION CONTROL: User group-based access restrictions

• Complex Business Features

  • Hierarchical Tree Structure: Unlimited depth parent-child relationships
  • Accounting Integration: Each expense type creates corresponding account in chart of accounts
  • Tree Type Classification: Different expense categories (0=مصروفات عمومية، 1=مصروفات ادارية، 2=مصروفات بيعية)
  • Supervision Workflows: Built-in approval ratios and amount limits
  • Save (Cash Box) Integration: Expense types can be restricted to specific cash boxes
  • User Group Permissions: Fine-grained access control per expense type
  • Dependency Management: Cannot delete types with child categories or existing expenses
  • Multi-language Interface: Complete Arabic localization
  • External API Integration: JSON responses for third-party system integration

• Data Validation & Business Rules

  • Name Validation: Expense type names must be unique within hierarchy level
  • Hierarchy Integrity: Parent-child relationships maintained with referential integrity
  • Dependency Checking: Cannot delete expense types with:
  • → Child expense types
  • → Associated expense records
  • Supervision Rules: Configurable approval workflows with ratio/amount limits
  • User Group Restrictions: Expense types can be restricted to specific user groups
  • Save Assignment: Expense types can be linked to specific cash boxes for control
  • Accounting Consistency: All expense types must have corresponding chart of accounts entries

■ 3. Database Schema

• Primary Table: expensestype


CREATE TABLE expensestype (
    expensestypeid INT PRIMARY KEY AUTO_INCREMENT,
    expensestypename VARCHAR(256) NOT NULL,
    expensestypedetails TEXT,
    expensestypedate DATE NOT NULL,
    conditions TINYINT NOT NULL DEFAULT 0,  -- 0=active, 1=deleted

    userid INT NOT NULL,
    parent INT NOT NULL DEFAULT 0,  -- parent expense type ID

    type INT NOT NULL DEFAULT 0,    -- expense classification

    saveid INT NOT NULL DEFAULT 0,  -- linked cash box/save

    addOnlyGroupIds VARCHAR(500),   -- comma-separated user group IDs

    treeType INT NOT NULL DEFAULT 0, -- tree classification (0,1,2)

    treeId INT NOT NULL DEFAULT 0,   -- corresponding account tree ID

    withinsupervision_ratio TINYINT NOT NULL DEFAULT 0, -- supervision required

    supervision_ratiotype INT NOT NULL DEFAULT 0,       -- supervision type

    supervision_amount DECIMAL(10,2) DEFAULT 0,         -- supervision limit

    webApiId INT NOT NULL DEFAULT 0 -- external system integration

);

• Related Tables

expenses - Actual Expense Records


CREATE TABLE expenses (
    expensesid INT PRIMARY KEY AUTO_INCREMENT,
    expensestypeid INT NOT NULL,  -- Foreign key to expensestype

    expensesValue DECIMAL(10,2) NOT NULL,
    expensesDetails TEXT,
    expensesDate DATE NOT NULL,
    conditions TINYINT NOT NULL DEFAULT 0,
    userid INT NOT NULL,
    saveid INT NOT NULL,
    dailyentryid INT NOT NULL DEFAULT 0
);

accountstree - Chart of Accounts Integration


CREATE TABLE accountstree (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(256) NOT NULL,
    customName VARCHAR(256),
    parent INT NOT NULL DEFAULT 0,
    level INT NOT NULL DEFAULT 0,
    conditions TINYINT NOT NULL DEFAULT 0,
    userid INT NOT NULL,
    creationDate DATE NOT NULL
);

save - Cash Boxes/Safes


CREATE TABLE save (
    saveid INT PRIMARY KEY AUTO_INCREMENT,
    savename VARCHAR(256) NOT NULL,
    saveamount DECIMAL(10,2) NOT NULL DEFAULT 0,
    conditions TINYINT NOT NULL DEFAULT 0,
    userid INT NOT NULL
);

usergroup - User Group Permissions


CREATE TABLE usergroup (
    usergroupid INT PRIMARY KEY AUTO_INCREMENT,
    usergroupname VARCHAR(256) NOT NULL,
    conditions TINYINT NOT NULL DEFAULT 0
);

• Relationships

  • expensestypeid → expenses: One-to-many expense records
  • parent → expensestypeid: Self-referencing parent-child hierarchy
  • treeId → accountstree: One-to-one accounting integration
  • saveid → save: Many-to-one cash box assignment
  • addOnlyGroupIds → usergroup: Many-to-many user group permissions
  • userid → user: Many-to-one user tracking

■ 4. Current Implementation Analysis

• SQL Operations from Current Controller

▪ Expense Type Creation SQL (add() function)


-- Insert new expense type (via ExpensestypeMySqlDAO.insert)

INSERT INTO expensestype (
    expensestypename, expensestypedetails, expensestypedate, conditions, userid, 
    parent, type, saveid, addOnlyGroupIds, treeType, treeId, 
    withinsupervision_ratio, supervision_ratiotype, supervision_amount, webApiId
) VALUES (?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

-- Create corresponding account in chart of accounts

INSERT INTO accountstree (name, parent, level, conditions, userid, creationDate)
VALUES (?, ?, 3, 0, ?, ?)

-- Update expense type with tree ID

UPDATE expensestype SET treeId = ? WHERE expensestypeid = ?

▪ Expense Type Listing SQL (show() function)


-- Get hierarchical expense types without expenses (from getTypesWithoutExpenses)

SELECT expensestype.*
FROM expensestype
LEFT JOIN expenses ON expenses.expensestypeid = expensestype.expensestypeid
WHERE expenses.expensestypeid IS NULL
  AND expensestype.conditions = 0
  AND expensestype.parent = ?
  
-- With save filtering:

-- Additional condition: AND (expensestype.saveid = 0 OR expensestype.saveid IN (?))

-- With specific save: AND expensestype.saveid = ?


-- Search by name

SELECT * FROM expensestype 
WHERE conditions = 0 
  AND expensestypename LIKE ?
ORDER BY expensestypeid DESC

▪ Expense Type Update SQL (update() function)


-- Load existing data

SELECT * FROM expensestype WHERE expensestypeid = ?

-- Update expense type

UPDATE expensestype SET 
    expensestypename = ?, expensestypedetails = ?, expensestypedate = ?, 
    conditions = ?, userid = ?, parent = ?, type = ?, saveid = ?, 
    addOnlyGroupIds = ?, treeType = ?, treeId = ?, 
    withinsupervision_ratio = ?, supervision_ratiotype = ?, supervision_amount = ?
WHERE expensestypeid = ?

-- Update corresponding account tree

UPDATE accountstree SET 
    name = ?, customName = ?, parent = ?
WHERE id = ?

▪ Expense Type Delete Operations SQL


-- Check for expense dependencies

SELECT * FROM expenses WHERE expensestypeid = ?

-- Check for child expense types

SELECT * FROM expensestype WHERE parent = ?

-- If no dependencies, soft delete (via updateConditions)

UPDATE expensestype SET 
    expensestypedate = ?, conditions = 1, userid = ?
WHERE expensestypeid = ?

-- Delete from account tree

DELETE FROM accountstree WHERE name = ?

▪ Hierarchical Tree Queries SQL


-- Get expense types by parent (for tree building)

SELECT *
FROM expensestype
WHERE parent = ? AND conditions = 0
ORDER BY expensestypeid

-- Get root level expense types

SELECT *
FROM expensestype
WHERE parent = 0 AND conditions = 0

-- Get expense types without children (leaf nodes)

SELECT *
FROM expensestype
WHERE conditions = 0
  AND expensestypeid NOT IN (
    SELECT DISTINCT parent
    FROM expensestype
    WHERE conditions = 0 AND parent <> 0
  )
ORDER BY expensestypeid DESC

-- Get expense types with parent names

SELECT expensestype.*, theParent.expensestypename as parentexpensestypename
FROM expensestype
LEFT JOIN expensestype theParent ON expensestype.parent = theParent.expensestypeid
WHERE expensestype.conditions = 0

▪ Support Queries SQL


-- Get all saves/cash boxes

SELECT * FROM save WHERE conditions = 0

-- Get saves for user

SELECT * FROM save WHERE saveid IN (?) AND conditions = 0

-- Get user groups

SELECT * FROM usergroup WHERE conditions = 0

-- Sum expenses by type

SELECT expensestype.expensestypename, expensestype.conditions, 
       SUM(expenses.expensesValue) as sumExpensesValue
FROM expensestype
JOIN expenses ON expensestype.expensestypeid = expenses.expensestypeid
WHERE expenses.conditions = 0
GROUP BY expensestype.expensestypeid

■ 5. All Controller Operations (Complete List - 8 Operations)

Operation #1: Default (empty 'do')

URL: expensesTypeController.php Method: GET Purpose: Display add expense type form with hierarchical parents Template: expensesTypeview/add.html SQL Operations:

-- Build hierarchical tree (via orderExtepensesTypeParentsAsTree)

SELECT expensestype.*
FROM expensestype
LEFT JOIN expenses ON expenses.expensestypeid = expensestype.expensestypeid
WHERE expenses.expensestypeid IS NULL
  AND expensestype.conditions = 0
  AND expensestype.parent = ?

-- Get available saves

SELECT * FROM save WHERE conditions = 0

-- Get user groups

SELECT * FROM usergroup WHERE conditions = 0
Business Logic:
  • → Builds hierarchical tree of expense types for parent selection
  • → Filters by user's save permissions
  • → Loads user groups for permission assignment

Operation #2: do=add

URL: expensesTypeController.php?do=add Method: POST Purpose: Create new expense type with accounting integration SQL Operations:

-- Insert expense type

INSERT INTO expensestype (
    expensestypename, expensestypedetails, expensestypedate, conditions, userid, 
    parent, type, saveid, addOnlyGroupIds, treeType, treeId, 
    withinsupervision_ratio, supervision_ratiotype, supervision_amount, webApiId
) VALUES (?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

-- Get parent tree ID if parent exists

SELECT treeId FROM expensestype WHERE expensestypeid = ?

-- Create account tree entry

INSERT INTO accountstree (name, parent, level, conditions, userid, creationDate)
VALUES (?, ?, 3, 0, ?, ?)

-- Update with tree ID

UPDATE expensestype SET treeId = ? WHERE expensestypeid = ?
Business Logic:
  • → Creates hierarchical expense type with parent relationship
  • → Integrates with chart of accounts via addTreeElement()
  • → Handles user group permissions
  • → Supports supervision workflow configuration
  • → Returns JSON for CURL requests

Operation #3: do=show

URL: expensesTypeController.php?do=show Method: GET Purpose: Display hierarchical expense types with filtering Template: expensesTypeview/show.html SQL Operations:

-- Build hierarchical tree

SELECT expensestype.*
FROM expensestype
LEFT JOIN expenses ON expenses.expensestypeid = expensestype.expensestypeid
WHERE expenses.expensestypeid IS NULL
  AND expensestype.conditions = 0
  AND expensestype.parent = ?

-- Filter by name if specified

SELECT * FROM expensestype 
WHERE conditions = 0 
  AND expensestypename LIKE ?
ORDER BY expensestypeid DESC
Business Logic:
  • → Shows expense types in hierarchical tree structure
  • → Supports filtering by parent name
  • → Recursive tree building with level indicators
  • → YouTube integration for training videos

Operation #4: do=executeOperation

URL: expensesTypeController.php?do=executeOperation Method: POST Purpose: Bulk operations on selected expense types SQL Operations:

-- For bulk delete (operation=1):

-- Check dependencies for each expense type:

SELECT * FROM expenses WHERE expensestypeid = ?
SELECT * FROM expensestype WHERE parent = ?

-- If no dependencies, soft delete:

UPDATE expensestype SET 
    expensestypedate = ?, conditions = 1, userid = ?
WHERE expensestypeid = ?
Business Logic:
  • → Processes arrays of expense type IDs
  • → Validates each delete operation for dependencies
  • → Provides detailed feedback for each expense type
  • → Blocks deletion if type has child types or expenses

Operation #5: do=editprint

URL: expensesTypeController.php?do=editprint&id={expensestypeid} Method: GET Purpose: Print-friendly edit view Template: expensesTypeview/editprint.html SQL Operations:

-- Load expense type data

SELECT * FROM expensestype WHERE expensestypeid = ?

-- Build parent hierarchy for selection

SELECT expensestype.*
FROM expensestype
LEFT JOIN expenses ON expenses.expensestypeid = expensestype.expensestypeid
WHERE expenses.expensestypeid IS NULL
  AND expensestype.conditions = 0
  AND expensestype.expensestypeid != ?

-- Load saves

SELECT * FROM save WHERE conditions = 0
Business Logic: Same as edit but with print styling

Operation #6: do=edit

URL: expensesTypeController.php?do=edit&id={expensestypeid} Method: GET Purpose: Display edit form for expense type Template: expensesTypeview/edit.html SQL Operations:

-- Load expense type data

SELECT * FROM expensestype WHERE expensestypeid = ?

-- Build parent hierarchy (excluding current type to prevent circular references)

SELECT expensestype.*
FROM expensestype
LEFT JOIN expenses ON expenses.expensestypeid = expensestype.expensestypeid
WHERE expenses.expensestypeid IS NULL
  AND expensestype.conditions = 0
  AND expensestype.expensestypeid != ?

-- Load saves with user permissions

SELECT * FROM save WHERE saveid IN (?) AND conditions = 0

-- Load user groups

SELECT * FROM usergroup WHERE conditions = 0
Business Logic:
  • → Loads expense type for editing
  • → Excludes current type from parent hierarchy to prevent cycles
  • → Parses user group permissions from comma-separated string
  • → Handles save filtering based on user permissions

Operation #7: do=update

URL: expensesTypeController.php?do=update Method: POST Purpose: Update existing expense type with tree repositioning SQL Operations:

-- Load existing data

SELECT * FROM expensestype WHERE expensestypeid = ?

-- Update expense type

UPDATE expensestype SET 
    expensestypename = ?, expensestypedetails = ?, expensestypedate = ?, 
    conditions = ?, userid = ?, parent = ?, type = ?, saveid = ?, 
    addOnlyGroupIds = ?, treeType = ?, treeId = ?, 
    withinsupervision_ratio = ?, supervision_ratiotype = ?, supervision_amount = ?
WHERE expensestypeid = ?

-- Get parent data for tree update

SELECT treeId FROM expensestype WHERE expensestypeid = ?

-- Update account tree

SELECT * FROM accountstree WHERE id = ?
UPDATE accountstree SET name = ?, customName = ?, parent = ? WHERE id = ?
Business Logic:
  • → Updates expense type with hierarchy repositioning
  • → Maintains chart of accounts synchronization via editTreeElement()
  • → Handles user group permission updates
  • → Returns JSON for CURL requests

Operation #8: do=delete

URL: expensesTypeController.php?do=delete&id={expensestypeid} Method: GET Purpose: Delete expense type with comprehensive dependency checking SQL Operations:

-- Check for expense dependencies

SELECT * FROM expenses WHERE expensestypeid = ?

-- Check for child expense types

SELECT * FROM expensestype WHERE parent = ?

-- If no dependencies exist:

-- Load expense type for tree deletion

SELECT * FROM expensestype WHERE expensestypeid = ?

-- Delete from account tree

DELETE FROM accountstree WHERE name = ?

-- Soft delete expense type

UPDATE expensestype SET 
    expensestypedate = ?, conditions = 1, userid = ?
WHERE expensestypeid = ?
Business Logic:
  • → Comprehensive dependency validation
  • → Cannot delete if has child expense types
  • → Cannot delete if has associated expense records
  • → Removes from chart of accounts via delTreeElement()
  • → Shows detailed Arabic error messages
  • → Supports JSON responses for API integration

■ 5. API Specification

• Base URL Structure


/api/v1/expense-types

• RESTful Endpoints

▪ 1. Get All Expense Types


GET /api/v1/expense-types
Query Parameters:
  • page: Page number (default: 1)
  • limit: Items per page (default: 20, max: 100)
  • search: Search in expense type name
  • parent_id: Filter by parent expense type
  • tree_type: Filter by tree classification (0, 1, 2)
  • save_id: Filter by cash box assignment
  • conditions: Filter by status (0=active, 1=deleted)
  • with_hierarchy: Boolean, return hierarchical tree structure
  • leaf_only: Boolean, return only leaf nodes (no children)
Response:

{
  "success": true,
  "data": [
    {
      "expensestypeid": 1,
      "expensestypename": "مصروفات عمومية",
      "expensestypedetails": "المصروفات الأساسية للشركة",
      "expensestypedate": "2024-01-15",
      "conditions": 0,
      "userid": 1,
      "parent": 0,
      "type": 0,
      "saveid": 1,
      "addOnlyGroupIds": "1,2,3",
      "treeType": 0,
      "treeId": 414,
      "withinsupervision_ratio": 1,
      "supervision_ratiotype": 1,
      "supervision_amount": 5000.00,
      "webApiId": 0,
      "created_by": "Admin User",
      "parent_name": null,
      "save_name": "الخزنة الرئيسية",
      "children_count": 5,
      "has_expenses": false,
      "hierarchy_level": 0,
      "user_groups": [
        {"id": 1, "name": "مديرين"},
        {"id": 2, "name": "محاسبين"}
      ]
    }
  ],
  "hierarchy": [
    {
      "expensestypeid": 1,
      "expensestypename": "مصروفات عمومية",
      "level": 0,
      "children": [
        {
          "expensestypeid": 5,
          "expensestypename": "مصروفات كهرباء",
          "level": 1,
          "children": []
        }
      ]
    }
  ],
  "pagination": {
    "current_page": 1,
    "total_pages": 8,
    "total_items": 147,
    "per_page": 20
  }
}

▪ 2. Get Single Expense Type


GET /api/v1/expense-types/{id}
Response:

{
  "success": true,
  "data": {
    "expensestypeid": 1,
    "expensestypename": "مصروفات عمومية",
    "expensestypedetails": "المصروفات الأساسية للشركة",
    "expensestypedate": "2024-01-15",
    "conditions": 0,
    "userid": 1,
    "parent": 0,
    "type": 0,
    "saveid": 1,
    "treeType": 0,
    "treeId": 414,
    "supervision": {
      "withinsupervision_ratio": 1,
      "supervision_ratiotype": 1,
      "supervision_amount": 5000.00
    },
    "permissions": {
      "addOnlyGroupIds": "1,2,3",
      "user_groups": [
        {"id": 1, "name": "مديرين"},
        {"id": 2, "name": "محاسبين"}
      ]
    },
    "relationships": {
      "parent": {
        "expensestypeid": 0,
        "expensestypename": null
      },
      "children": [
        {
          "expensestypeid": 5,
          "expensestypename": "مصروفات كهرباء"
        }
      ],
      "save": {
        "saveid": 1,
        "savename": "الخزنة الرئيسية"
      }
    },
    "accounting": {
      "treeId": 414,
      "account_name": "مصروفات عمومية",
      "account_parent": 413
    },
    "statistics": {
      "children_count": 5,
      "total_expenses": 125000.50,
      "expense_records_count": 245
    },
    "created_by": "Admin User",
    "webApiId": 0
  }
}

▪ 3. Create Expense Type


POST /api/v1/expense-types
Request Body:

{
  "expensestypename": "مصروفات جديدة",
  "expensestypedetails": "وصف نوع المصروف",
  "parent": 1,
  "type": 0,
  "saveid": 1,
  "treeType": 0,
  "supervision": {
    "withinsupervision_ratio": 1,
    "supervision_ratiotype": 1,
    "supervision_amount": 1000.00
  },
  "addOnlyGroupIds": [1, 2, 3],
  "webApiId": 0
}
Response:

{
  "success": true,
  "message": "تم إنشاء نوع المصروف بنجاح",
  "message_en": "Expense type created successfully",
  "data": {
    "expensestypeid": 25,
    "expensestypename": "مصروفات جديدة",
    "treeId": 455,
    "account_created": true,
    "hierarchy_level": 1,
    "parent_name": "مصروفات عمومية"
  }
}

▪ 4. Update Expense Type


PUT /api/v1/expense-types/{id}
Request Body: (Same as create) Response:

{
  "success": true,
  "message": "تم تحديث نوع المصروف بنجاح",
  "message_en": "Expense type updated successfully",
  "data": {
    "expensestypeid": 25,
    "expensestypename": "مصروفات محدثة",
    "hierarchy_changed": true,
    "account_updated": true
  }
}

▪ 5. Delete Expense Type


DELETE /api/v1/expense-types/{id}
Response Success:

{
  "success": true,
  "message": "تم حذف نوع المصروف بنجاح",
  "message_en": "Expense type deleted successfully"
}
Response Error (Has Dependencies):

{
  "success": false,
  "error": {
    "code": "EXPENSE_TYPE_HAS_DEPENDENCIES",
    "message": "لا يمكن حذف هذا النوع لأنه مرتبط ببيانات أخرى",
    "message_en": "Cannot delete this expense type as it has related data",
    "details": {
      "child_types": 3,
      "expense_records": 15,
      "blocking_reasons": [
        "Has 3 child expense types",
        "Has 15 expense records"
      ]
    }
  }
}

▪ 6. Get Expense Type Hierarchy


GET /api/v1/expense-types/hierarchy
Query Parameters:
  • save_id: Filter by cash box
  • tree_type: Filter by tree classification
  • exclude_id: Exclude specific expense type (for parent selection)
Response:

{
  "success": true,
  "data": {
    "tree": [
      {
        "expensestypeid": 1,
        "expensestypename": "مصروفات عمومية",
        "level": 0,
        "has_children": true,
        "children": [
          {
            "expensestypeid": 5,
            "expensestypename": "_مصروفات كهرباء",
            "level": 1,
            "has_children": false,
            "children": []
          }
        ]
      }
    ],
    "flat_list": [
      {
        "expensestypeid": 1,
        "expensestypename": "مصروفات عمومية",
        "level": 0,
        "formatted_name": "مصروفات عمومية"
      },
      {
        "expensestypeid": 5,
        "expensestypename": "مصروفات كهرباء", 
        "level": 1,
        "formatted_name": "_ مصروفات كهرباء"
      }
    ]
  }
}

▪ 7. Bulk Operations


POST /api/v1/expense-types/bulk
Request Body:

{
  "operation": "delete",
  "expense_type_ids": [5, 7, 9, 12]
}
Response:

{
  "success": true,
  "message": "تمت العملية المجمعة بنجاح",
  "results": [
    {
      "expensestypeid": 5,
      "expensestypename": "مصروفات كهرباء",
      "status": "success",
      "message": "تم الحذف بنجاح"
    },
    {
      "expensestypeid": 7,
      "expensestypename": "مصروفات مياه",
      "status": "error",
      "message": "لا يمكن حذف هذا النوع لأنه مرتبط ببيانات أخرى",
      "details": {
        "child_types": 2,
        "expense_records": 5
      }
    }
  ],
  "summary": {
    "total": 4,
    "successful": 3,
    "failed": 1
  }
}

▪ 8. Search Expense Types


GET /api/v1/expense-types/search
Query Parameters:
  • q: Search query
  • save_id: Filter by cash box
  • leaf_only: Boolean, only leaf nodes
  • limit: Maximum results (default: 50)
Response:

{
  "success": true,
  "data": [
    {
      "expensestypeid": 15,
      "expensestypename": "مصروفات كهرباء",
      "parent_name": "مصروفات عمومية",
      "hierarchy_path": "مصروفات عمومية > مصروفات كهرباء",
      "level": 1,
      "has_children": false,
      "total_expenses": 25000.50
    }
  ]
}

• HTTP Status Codes

  • 200: Success (GET, PUT)
  • 201: Created (POST)
  • 204: No Content (DELETE)
  • 400: Bad Request (validation errors)
  • 401: Unauthorized
  • 403: Forbidden
  • 404: Expense Type Not Found
  • 409: Conflict (duplicate name in same hierarchy level)
  • 422: Unprocessable Entity (business rule violations)
  • 500: Internal Server Error

• Error Response Format


{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "البيانات المدخلة غير صحيحة",
    "message_en": "The given data was invalid",
    "details": {
      "expensestypename": ["اسم نوع المصروف مطلوب"],
      "parent": ["نوع المصروف الأب غير موجود"],
      "saveid": ["الخزنة المحددة غير صحيحة"]
    }
  }
}

■ 6. Authentication & Authorization

• Authentication Method

  • JWT Tokens: For API access with user identification
  • Session Integration: Compatible with existing session system

• Required Permissions

  • expense_types.read: View expense types
  • expense_types.create: Add new expense types
  • expense_types.update: Edit expense type information
  • expense_types.delete: Delete expense types
  • expense_types.bulk: Perform bulk operations
  • expense_types.tree: Manage hierarchical structure
  • accounting.integration: Modify chart of accounts

• Rate Limiting

  • Standard Users: 120 requests per minute
  • Premium Users: 300 requests per minute
  • Bulk Operations: 10 requests per minute

■ 7. Implementation Guidelines

• Migration Steps

  1. Database Analysis: Review hierarchical structure and accounting integration
  2. API Design: Implement RESTful endpoints with tree structure support
  3. Accounting Integration: Maintain chart of accounts synchronization
  4. Hierarchy Management: Implement tree building and validation
  5. Data Migration: Ensure data integrity during transition
  6. Testing: Comprehensive testing with existing hierarchical data

• Critical Validations


// Expense type creation validation

$rules = [
    &#039;expensestypename&#039; => &#039;required|string|max:256&#039;,

    &#039;expensestypedetails&#039; => &#039;nullable|string|max:1000&#039;,

    &#039;parent&#039; => &#039;nullable|integer|exists:expensestype,expensestypeid&#039;,

    &#039;type&#039; => &#039;integer|in:0,1,2&#039;,

    &#039;saveid&#039; => &#039;required|integer|exists:save,saveid&#039;,

    &#039;treeType&#039; => &#039;integer|in:0,1,2&#039;,

    &#039;addOnlyGroupIds&#039; => &#039;array&#039;,

    &#039;addOnlyGroupIds.*&#039; => &#039;integer|exists:usergroup,usergroupid&#039;,

    &#039;supervision_amount&#039; => &#039;numeric|min:0&#039;

];

// Business rule validations

">if ($parent && !ExpenseType::where(&#039;expensestypeid&#039;, $parent)->where(&#039;conditions&#039;, 0)->exists()) {

    throw new ValidationException(&#039;Parent expense type not found or inactive&#039;);

}

// Prevent circular references

">if ($parent && $this->wouldCreateCircularReference(">$expensestypeid, $parent)) {
    throw new ValidationException(&#039;Cannot set parent - would create circular reference&#039;);

}

// Dependency check before deletion

$childCount = ExpenseType::where(&#039;parent&#039;, $expensestypeid)->count();

$expenseCount = Expense::where(&#039;expensestypeid&#039;, $expensestypeid)->count();

">if (">$childCount > 0 || $expenseCount > 0) {
    throw new BusinessException(&#039;Cannot delete expense type - has dependencies&#039;);

}

• API Endpoint to SQL Mapping

▪ GET /api/v1/expense-types (List Expense Types)

Implementation SQL:

-- Base hierarchical query (from orderExtepensesTypeParentsAsTree)

SELECT expensestype.*
FROM expensestype
LEFT JOIN expenses ON expenses.expensestypeid = expensestype.expensestypeid
WHERE expenses.expensestypeid IS NULL
  AND expensestype.conditions = 0
  AND expensestype.parent = ?
ORDER BY expensestype.expensestypeid

-- With filters:

-- Save filter: AND (expensestype.saveid = 0 OR expensestype.saveid IN (?))

-- Tree type filter: AND expensestype.treeType = ?

-- Search filter: AND expensestype.expensestypename LIKE "%?%"

▪ POST /api/v1/expense-types (Create Expense Type)

Implementation SQL (Transaction):

BEGIN TRANSACTION;

-- Insert expense type

INSERT INTO expensestype (
    expensestypename, expensestypedetails, expensestypedate, conditions, userid, 
    parent, type, saveid, addOnlyGroupIds, treeType, treeId, 
    withinsupervision_ratio, supervision_ratiotype, supervision_amount, webApiId
) VALUES (?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);

SET @expense_type_id = LAST_INSERT_ID();

-- Get parent tree ID if exists

IF (@parent_id > 0) THEN
    SELECT treeId INTO @parent_tree_id FROM expensestype WHERE expensestypeid = @parent_id;
ELSE
    -- Set default parent based on tree type

    SET @parent_tree_id = CASE @tree_type
        WHEN 0 THEN 414  -- مصروفات عمومية

        WHEN 1 THEN 413  -- مصروفات ادارية  

        WHEN 2 THEN 412  -- مصروفات بيعية

    END;
END IF;

-- Create account tree entry

INSERT INTO accountstree (name, parent, level, conditions, userid, creationDate)
VALUES (?, @parent_tree_id, 3, 0, ?, ?);

SET @tree_id = LAST_INSERT_ID();

-- Update expense type with tree ID

UPDATE expensestype SET treeId = @tree_id WHERE expensestypeid = @expense_type_id;

COMMIT;

▪ PUT /api/v1/expense-types/{id} (Update Expense Type)

Implementation SQL:

BEGIN TRANSACTION;

-- Load existing data

SELECT * FROM expensestype WHERE expensestypeid = ?;

-- Update expense type

UPDATE expensestype SET 
    expensestypename = ?, expensestypedetails = ?, expensestypedate = ?, 
    userid = ?, parent = ?, type = ?, saveid = ?, 
    addOnlyGroupIds = ?, treeType = ?, 
    withinsupervision_ratio = ?, supervision_ratiotype = ?, supervision_amount = ?
WHERE expensestypeid = ?;

-- Update account tree

UPDATE accountstree SET name = ?, parent = ? WHERE id = ?;

COMMIT;

▪ DELETE /api/v1/expense-types/{id} (Delete Expense Type)

Implementation SQL:

-- Check dependencies

SELECT COUNT(*) as child_count FROM expensestype WHERE parent = ?;
SELECT COUNT(*) as expense_count FROM expenses WHERE expensestypeid = ?;

-- If no dependencies, delete

IF (@child_count = 0 AND @expense_count = 0) THEN
    -- Get expense type name for tree deletion

    SELECT expensestypename INTO @type_name FROM expensestype WHERE expensestypeid = ?;
    
    -- Delete from account tree

    DELETE FROM accountstree WHERE name = @type_name;
    
    -- Soft delete expense type

    UPDATE expensestype SET 
        expensestypedate = ?, conditions = 1, userid = ?
    WHERE expensestypeid = ?;
END IF;

▪ GET /api/v1/expense-types/hierarchy (Get Hierarchy)

Implementation SQL:

-- Recursive hierarchy building

WITH RECURSIVE expense_tree AS (
    -- Root level

    SELECT expensestypeid, expensestypename, parent, 0 as level
    FROM expensestype 
    WHERE parent = 0 AND conditions = 0
    
    UNION ALL
    
    -- Child levels

    SELECT e.expensestypeid, e.expensestypename, e.parent, t.level + 1
    FROM expensestype e
    JOIN expense_tree t ON e.parent = t.expensestypeid
    WHERE e.conditions = 0
)
SELECT * FROM expense_tree ORDER BY level, expensestypeid;

• Database Optimizations


-- Essential indexes for expense types

CREATE INDEX idx_expensestype_name ON expensestype(expensestypename);
CREATE INDEX idx_expensestype_parent ON expensestype(parent);
CREATE INDEX idx_expensestype_conditions ON expensestype(conditions);
CREATE INDEX idx_expensestype_saveid ON expensestype(saveid);
CREATE INDEX idx_expensestype_treetype ON expensestype(treeType);
CREATE INDEX idx_expensestype_treeid ON expensestype(treeId);

-- Hierarchy query optimization

CREATE INDEX idx_expensestype_hierarchy ON expensestype(parent, conditions, expensestypeid);

-- Expense relationship index

CREATE INDEX idx_expenses_type ON expenses(expensestypeid, conditions);

-- Account tree integration

CREATE INDEX idx_accountstree_name ON accountstree(name);
CREATE INDEX idx_accountstree_parent ON accountstree(parent);

-- Daily entry system indexes

CREATE INDEX idx_dailyentry_date ON dailyentry(dailyentrydate);
CREATE INDEX idx_dailyentry_conditions ON dailyentry(conditions);
CREATE INDEX idx_dailyentrydebtor_entry ON dailyentrydebtor(dailyentryid);
CREATE INDEX idx_dailyentrydebtor_account ON dailyentrydebtor(accountstreeid);
CREATE INDEX idx_dailyentrycreditor_entry ON dailyentrycreditor(dailyentryid);
CREATE INDEX idx_dailyentrycreditor_account ON dailyentrycreditor(accountstreeid);

■ 11. Daily Entry System Integration

• Database Tables for Daily Entry

dailyentry - Journal Entry Header


CREATE TABLE dailyentry (
    dailyentryid INT PRIMARY KEY AUTO_INCREMENT,
    dailyentrydate DATE NOT NULL,
    dailyentrycomment TEXT,
    dailyentrytotalvalue DECIMAL(15,2) NOT NULL DEFAULT 0,
    conditions TINYINT NOT NULL DEFAULT 0,  -- 0=active, 1=deleted

    userid INT NOT NULL,
    creationDate DATETIME NOT NULL,
    operationId INT NOT NULL DEFAULT 0,
    operationDetailLink VARCHAR(500),
    costCenterID INT NOT NULL DEFAULT 0
);

dailyentrydebtor - Debit Entries


CREATE TABLE dailyentrydebtor (
    dailyentrydebtoreid INT PRIMARY KEY AUTO_INCREMENT,
    dailyentryid INT NOT NULL,  -- Foreign key to dailyentry

    accountstreeid INT NOT NULL, -- Foreign key to accountstree

    dailyentrydebtorevalue DECIMAL(15,2) NOT NULL,
    dailyentrydebtorecomment TEXT,
    conditions TINYINT NOT NULL DEFAULT 0,
    costCenterID INT NOT NULL DEFAULT 0
);

dailyentrycreditor - Credit Entries


CREATE TABLE dailyentrycreditor (
    dailyentrycreditorid INT PRIMARY KEY AUTO_INCREMENT,
    dailyentryid INT NOT NULL,  -- Foreign key to dailyentry

    accountstreeid INT NOT NULL, -- Foreign key to accountstree

    dailyentrycreditorvalue DECIMAL(15,2) NOT NULL,
    dailyentrycreditorcomment TEXT,
    conditions TINYINT NOT NULL DEFAULT 0,
    costCenterID INT NOT NULL DEFAULT 0
);

• Daily Entry Integration Functions

▪ Core Daily Entry Functions (from dailyentryfun.php)


/
  • ◆ Creates journal entries with debtor and creditor records
  • ◆ This is the core function that maintains double-entry bookkeeping
*/
function insertEntery($dailyEntryObj, $dailyEntryDebtorArray, $dailyEntryCreditorArray, $stopEntryTransaction = 0, $operationId = 0, $operationDetailLink = &#039;&#039;) { // Creates dailyentry record // Creates multiple dailyentrydebtor records // Creates multiple dailyentrycreditor records // Calls affectAccount() for each account affected } /
  • ◆ Creates or updates account tree elements for expense types
*/
function addTreeElement(">$name, $parent, ">$itemtype, $itemfrom, $itemtype2, ">$notes = &#039;&#039;, $theOrder = 0, ">$theValue = 0, $reportid = 0) { // itemtype: 0=expenses, 1=liabilities, 2=revenue, 3=assets, 4=capital // itemfrom: 0=from program, 1=from tree only // itemtype2: 0=parent node, 1=end element (used in entries) } /
  • ◆ Updates account balances based on transaction type
*/
">function affectAccount($CreditorOrDebtorObj, $type) { // type: &#039;debtor&#039; or &#039;creditor&#039; // Updates accountstree.theValue based on account type and transaction }

• Expense Type Creation with Daily Entry Integration

When an expense type is created, the system:
  1. Creates Expense Type Record

INSERT INTO expensestype (...) VALUES (...)
  1. Creates Account Tree Entry (via addTreeElement())

INSERT INTO accountstree (
    name, parent, itemtype, itemfrom, itemtype2, 
    accountNature, notes, theValue, userid, sysdate, reportid
) VALUES (?, ?, 0, 0, 1, ?, ?, 0, ?, ?, ?)
  1. Updates Expense Type with Tree ID

UPDATE expensestype SET treeId = ? WHERE expensestypeid = ?

• Expense Transaction Workflow

When an actual expense is recorded against an expense type:
  1. Create Expense Record

INSERT INTO expenses (expensestypeid, expensesValue, ...) VALUES (...)
  1. Create Journal Entry (via insertEntery())

-- Create journal header

INSERT INTO dailyentry (
    dailyentrydate, dailyentrycomment, dailyentrytotalvalue, 
    conditions, userid, creationDate, operationId
) VALUES (?, ?, ?, 0, ?, ?, ?)

-- Create debit entry (expense account)

INSERT INTO dailyentrydebtor (
    dailyentryid, accountstreeid, dailyentrydebtorevalue, 
    dailyentrydebtorecomment, conditions
) VALUES (?, ?, ?, ?, 0)

-- Create credit entry (cash/bank account)

INSERT INTO dailyentrycreditor (
    dailyentryid, accountstreeid, dailyentrycreditorvalue, 
    dailyentrycreditorcomment, conditions
) VALUES (?, ?, ?, ?, 0)
  1. Update Account Balances (via affectAccount())

-- Update expense account balance (increase with debit)

UPDATE accountstree SET theValue = theValue + ? WHERE id = ?

-- Update cash/bank account balance (decrease with credit)

UPDATE accountstree SET theValue = theValue - ? WHERE id = ?

• Account Type Behavior in Daily Entry System


// From dailyentryfun.php - Account behavior rules

">function whatToDo(">$accountType, ">$numSign, $type) {
    // $accountType: 0=expenses, 1=liabilities, 2=revenue, 3=assets, 4=capital

    // $numSign: +/- amount

    // $type: &#039;debtor&#039; or &#039;creditor&#039;

    
    switch($accountType) {
        case 0: // مصروفات (Expenses)

        case 3: // أصول (Assets)

            // Debit increases, Credit decreases

            ">if ($type == &#039;debtor&#039;) return $numSign;      // +amount

            ">if ($type == &#039;creditor&#039;) return -$numSign;   // -amount

            break;
            
        case 1: // خصوم (Liabilities)

        case 2: // إيرادات (Revenue) 

        case 4: // رأس المال (Capital)

            // Debit decreases, Credit increases

            ">if ($type == &#039;debtor&#039;) return -$numSign;     // -amount

            ">if ($type == &#039;creditor&#039;) return $numSign;    // +amount

            break;
    }
}

• Plugin System Integration

The expense type system integrates with the plugin system (from affectplugins.php):

// Plugin mapping for expense types

$pluginMapArr = array(
    &#039;expenses&#039; => 411,  // Main expenses account tree ID

    &#039;save&#039; => 40,       // Cash accounts

    &#039;bank&#039; => 38        // Bank accounts

);

// When expense is recorded, plugin system routes to appropriate handlers

">function affectPlugin(">$whatIsIt, ">$val, ">$id, $operation, ">$elementName, $comment, 
                     $controllerName, $costCenterID, $accountstreeid, $whichSideisIt, 
                     $dailyEntry, $AllDailyEntryDebtor, $AllDailyEntryCreditor) {
    // Routes expense transactions to proper account handling

    // Updates cash/bank balances

    // Creates audit trail

}

• API Integration with Daily Entry

For API implementation, expense type operations must maintain daily entry integration:

// API Endpoint: POST /api/v1/expense-types/{id}/expenses

">public function createExpense(Request $request, $expenseTypeId) {
    DB::transaction(">function() ">use ($request, $expenseTypeId) {
        // 1. Create expense record

        $expense = Expense::create([
            &#039;expensestypeid&#039; => $expenseTypeId,

            &#039;expensesValue&#039; => $request->amount,

            &#039;expensesDetails&#039; => $request->description,

            &#039;saveid&#039; => $request->save_id,

            &#039;userid&#039; => Auth::id()

        ]);
        
        // 2. Get expense type and account tree info

        $expenseType = ExpenseType::with(&#039;accountTree&#039;)->find($expenseTypeId);

        ">$save = Save::find($request->save_id);
        
        // 3. Prepare daily entry data

        $dailyEntry = [
            &#039;dailyentrydate&#039; => date(&#039;Y-m-d&#039;),

            &#039;dailyentrycomment&#039; => "مصروف: {$expenseType->expensestypename}",

            &#039;dailyentrytotalvalue&#039; => $request->amount,

            &#039;operationId&#039; => $expense->expensesid,

            &#039;operationDetailLink&#039; => &#039;expenses&#039;

        ];
        
        $debtorArray = [
            [
                &#039;accountstreeid&#039; => $expenseType->treeId,  // Expense account

                &#039;value&#039; => $request->amount,

                &#039;comment&#039; => $request->description

            ]
        ];
        
        $creditorArray = [
            [
                &#039;accountstreeid&#039; => $save->accountTreeId,  // Cash/Bank account

                &#039;value&#039; => $request->amount,

                &#039;comment&#039; => "دفع مصروف: {$expenseType->expensestypename}"

            ]
        ];
        
        // 4. Create journal entry (calls insertEntery function)

        insertEntery($dailyEntry, $debtorArray, $creditorArray, 0, 
                    $expense->expensesid, &#039;expenses&#039;);

    });
}

■ 8. Examples

• cURL Examples

▪ Create Expense Type


curl -X POST http://localhost/api/v1/expense-types \

  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-jwt-token" \
  -d &#039;{

    "expensestypename": "مصروفات صيانة",
    "expensestypedetails": "مصروفات صيانة المعدات والأجهزة",
    "parent": 1,
    "type": 0,
    "saveid": 1,
    "treeType": 0,
    "supervision": {
      "withinsupervision_ratio": 1,
      "supervision_ratiotype": 1,
      "supervision_amount": 2000.00
    },
    "addOnlyGroupIds": [1, 2]
  }&#039;

▪ Get Hierarchical Tree


curl -X GET "http://localhost/api/v1/expense-types/hierarchy?save_id=1&tree_type=0" \

  -H "Authorization: Bearer your-jwt-token"

▪ Bulk Delete


curl -X POST http://localhost/api/v1/expense-types/bulk \

  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-jwt-token" \
  -d &#039;{

    "operation": "delete",
    "expense_type_ids": [5, 7, 9]
  }&#039;

• JavaScript Examples

▪ Expense Type Management Class


class ExpenseTypeAPI {
  constructor(baseURL, token) {
    this.baseURL = baseURL;
    this.token = token;
  }

  async getExpenseTypes(filters = {}) {
    const params = new URLSearchParams(filters);
    const response = ">await fetch(${this.baseURL}/api/v1/expense-types?${params}, {
      headers: { &#039;Authorization&#039;: Bearer ${this.token} }

    });
    return await response.json();
  }

  async getHierarchy(filters = {}) {
    const params = new URLSearchParams(filters);
    const response = ">await fetch(${this.baseURL}/api/v1/expense-types/hierarchy?${params}, {
      headers: { &#039;Authorization&#039;: Bearer ${this.token} }

    });
    return await response.json();
  }

  async createExpenseType(expenseTypeData) {
    const response = ">await fetch(${this.baseURL}/api/v1/expense-types, {
      method: &#039;POST&#039;,

      headers: {
        &#039;Content-Type&#039;: &#039;application/json&#039;,

        &#039;Authorization&#039;: Bearer ${this.token}

      },
      body: JSON.stringify(expenseTypeData)
    });
    return await response.json();
  }

  async updateExpenseType(expenseTypeId, updateData) {
    const response = ">await fetch(${this.baseURL}/api/v1/expense-types/${expenseTypeId}, {
      method: &#039;PUT&#039;,

      headers: {
        &#039;Content-Type&#039;: &#039;application/json&#039;,

        &#039;Authorization&#039;: Bearer ${this.token}

      },
      body: JSON.stringify(updateData)
    });
    return await response.json();
  }

  async deleteExpenseType(expenseTypeId) {
    const response = ">await fetch(${this.baseURL}/api/v1/expense-types/${expenseTypeId}, {
      method: &#039;DELETE&#039;,

      headers: { &#039;Authorization&#039;: Bearer ${this.token} }

    });
    return await response.json();
  }

  async bulkOperation(operation, expenseTypeIds) {
    const response = ">await fetch(${this.baseURL}/api/v1/expense-types/bulk, {
      method: &#039;POST&#039;,

      headers: {
        &#039;Content-Type&#039;: &#039;application/json&#039;,

        &#039;Authorization&#039;: Bearer ${this.token}

      },
      body: JSON.stringify({ 
        operation, 
        expense_type_ids: expenseTypeIds 
      })
    });
    return await response.json();
  }

  // Build hierarchical tree from flat data

  buildTree(flatData, parentId = 0) {
    const tree = [];
    for (const item of flatData) {
      if (item.parent === parentId) {
        const children = this.buildTree(flatData, item.expensestypeid);
        if (children.length > 0) {
          item.children = children;
        }
        tree.push(item);
      }
    }
    return tree;
  }
}

// Usage

">const expenseTypeAPI = new ExpenseTypeAPI(&#039;http://localhost&#039;, &#039;your-token&#039;);


// Get hierarchical expense types

const hierarchy = await expenseTypeAPI.getHierarchy({ save_id: 1 });
console.log(&#039;Expense Type Hierarchy:&#039;, hierarchy);


// Create new expense type

">const newExpenseType = await expenseTypeAPI.createExpenseType({
  expensestypename: &#039;مصروفات جديدة&#039;,

  parent: 1,
  saveid: 1,
  treeType: 0
});

■ 9. Future Enhancements

• Planned Features

  1. Advanced Tree Operations: Move subtrees, merge branches, duplicate hierarchies
  2. Budget Integration: Link expense types to budget allocations and tracking
  3. Workflow Automation: Advanced approval workflows based on amounts and types
  4. Expense Templates: Pre-configured expense type templates for different industries
  5. Multi-currency Support: Handle expense types in different currencies
  6. Advanced Reporting: Hierarchical expense analysis and trending
  7. API Versioning: Support for different API versions with backward compatibility
  8. Audit Trail: Complete change history for all expense type modifications
  9. Import/Export: Bulk expense type management via Excel/CSV
  10. Integration APIs: Third-party accounting system integrations

• API Versioning Strategy

  • URL Versioning: /api/v1/, /api/v2/
  • Backward Compatibility: Maintain v1 for 18 months after v2 release
  • Feature Flags: Gradual feature rollout

• Performance & Scalability


// Hierarchical caching strategy

Cache::remember("expense_type_hierarchy_{">$saveId}_{$treeType}", 1800, ">function() ">use (">$saveId, $treeType) {
    return ExpenseType::buildHierarchy(">$saveId, $treeType);
});

// Optimized tree building

$expenseTypes = ExpenseType::select(&#039;expensestypeid&#039;, &#039;expensestypename&#039;, &#039;parent&#039;, &#039;conditions&#039;)

    ->where(&#039;conditions&#039;, 0)

    ->where(">function($query) ">use ($saveId) {
        ">if ($saveId > 0) {
            $query->where(&#039;saveid&#039;, $saveId)->orWhere(&#039;saveid&#039;, 0);

        }
    })
    ->orderBy(&#039;parent&#039;)

    ->orderBy(&#039;expensestypeid&#039;)

    ->get();

// Recursive tree building with memoization

function buildTree(">$items, $parentId = 0, &$memo = []) {
    $key = "tree_{$parentId}";
    ">if (isset(">$memo[$key])) {
        return ">$memo[$key];
    }
    
    $tree = [];
    foreach (">$items as $item) {
        ">if ($item->parent == $parentId) {
            $children = buildTree(">$items, ">$item->expensestypeid, $memo);
            ">if (!empty($children)) {
                $item->children = $children;
            }
            $tree[] = $item;
        }
    }
    
    ">$memo[">$key] = $tree;
    return $tree;
}

■ 10. Testing Strategy

• Unit Tests

  • → Expense type CRUD operations
  • → Hierarchical tree building
  • → Dependency validation
  • → Accounting integration
  • → User permission handling

• Integration Tests

  • → API endpoint functionality
  • → Chart of accounts synchronization
  • → Tree structure validation
  • → Bulk operations
  • → JSON response formatting

• Performance Tests

  • → Large hierarchy handling
  • → Concurrent tree modifications
  • → Search functionality
  • → Recursive queries optimization

• Example Test Cases


">public function test_can_create_expense_type_with_accounting_integration()
{
    $expenseTypeData = [
        &#039;expensestypename&#039; => &#039;Test Expense Type&#039;,

        &#039;parent&#039; => 1,

        &#039;saveid&#039; => 1,

        &#039;treeType&#039; => 0,

        &#039;addOnlyGroupIds&#039; => [1, 2]

    ];

    $response = ">$this->postJson(&#039;/api/v1/expense-types&#039;, $expenseTypeData);


    $response->assertStatus(201)
            ->assertJson([
                &#039;success&#039; => true,

                &#039;data&#039; => [

                    &#039;expensestypename&#039; => &#039;Test Expense Type&#039;

                ]
            ]);

    // Verify database records

    $this->assertDatabaseHas(&#039;expensestype&#039;, [

        &#039;expensestypename&#039; => &#039;Test Expense Type&#039;

    ]);
    
    // Verify accounting integration

    $this->assertDatabaseHas(&#039;accountstree&#039;, [

        &#039;name&#039; => &#039;Test Expense Type&#039;

    ]);
}

">public function test_cannot_delete_expense_type_with_children()
{
    $parent = ExpenseType::factory()->create();
    $child = ExpenseType::factory()->create([&#039;parent&#039; => $parent->expensestypeid]);


    $response = ">$this->deleteJson("/api/v1/expense-types/{$parent->expensestypeid}");

    $response->assertStatus(422)
            ->assertJson([
                &#039;success&#039; => false,

                &#039;error&#039; => [

                    &#039;code&#039; => &#039;EXPENSE_TYPE_HAS_DEPENDENCIES&#039;

                ]
            ]);
}

">public function test_builds_hierarchical_tree_correctly()
{
    $parent1 = ExpenseType::factory()->create([&#039;parent&#039; => 0]);

    $parent2 = ExpenseType::factory()->create([&#039;parent&#039; => 0]);

    $child1 = ExpenseType::factory()->create([&#039;parent&#039; => $parent1->expensestypeid]);

    $child2 = ExpenseType::factory()->create([&#039;parent&#039; => $parent1->expensestypeid]);


    $response = $this->getJson(&#039;/api/v1/expense-types/hierarchy&#039;);


    $response->assertStatus(200)
            ->assertJsonStructure([
                &#039;success&#039;,

                &#039;data&#039; => [

                    &#039;tree&#039; => [

                        &#039;*&#039; => [

                            &#039;expensestypeid&#039;,

                            &#039;expensestypename&#039;, 

                            &#039;level&#039;,

                            &#039;children&#039;

                        ]
                    ]
                ]
            ]);

    $tree = $response->json(&#039;data.tree&#039;);

    $this->assertCount(2, $tree); // Two root level items

    $this->assertCount(2, $tree[0][&#039;children&#039;]); // Parent1 has 2 children

}
---

■ ✅ COMPLETENESS VERIFICATION

Total Operations Documented: 8/8 (100% Complete)

  1. Default (empty 'do') - Display add form with hierarchical parents
  2. do=add - Create expense type with accounting integration
  3. do=show - Display hierarchical expense types with filtering
  4. do=executeOperation - Bulk operations with dependency checking
  5. do=editprint - Print-friendly edit view
  6. do=edit - Display edit form with hierarchy management
  7. do=update - Update expense type with tree repositioning
  8. do=delete - Delete with comprehensive dependency validation

Mathematical Verification:

  • → Operations in Controller: 8
  • → Operations in Documentation: 8
  • Completeness: 8/8 = 100%

SQL Operations Coverage:

  • → ✅ All DAO methods documented with actual SQL from both base and extended classes
  • → ✅ All controller functions mapped to SQL operations
  • → ✅ Complete hierarchical tree building logic included
  • → ✅ Accounting integration via addTreeElement() and editTreeElement() documented
  • → ✅ All dependency checking logic included

Integration Coverage:

  • → ✅ ERP Hierarchy integration (Financial Management → Expense Types)
  • → ✅ Daily entry system integration (dailyentryfun.php)
  • → ✅ Chart of accounts integration (accountstree)
  • → ✅ CURL integration for external systems
  • → ✅ Multi-language support (Arabic localization)
  • → ✅ User group permission system
  • → ✅ Save (cash box) management integration
--- This comprehensive documentation provides everything needed to convert the Expenses Type Controller to a modern REST API while maintaining all existing functionality including hierarchical tree management, accounting integration, dependency checking, supervision workflows, user permissions, and Arabic localization. The API design follows RESTful principles and accommodates all complex business requirements of the ERP system.