Expensesajex Documentation
Expenses Controller Ajax Documentation
File: /controllers/expensesControllerajex.php
Purpose: Ajax-based expense management with accounting integration, cost center allocation, and third-party API integration
Last Updated: December 20, 2024
Total Functions: 20+
Lines of Code: ~794
---
๐ Overview
The Expenses Controller Ajax provides comprehensive expense management functionality with real-time Ajax capabilities. It handles:
- โข Dynamic expense type management and Ajax search functionality
- โข Complex accounting journal entry creation for expenses
- โข Cash register/safe integration with balance validation
- โข Cost center allocation and tracking
- โข Multi-level approval and deletion workflows
- โข Third-party transportation API integration (Africano system)
- โข Employee and doctor expense tracking
- โข Advanced search and filtering with user permission controls
Primary Functions
- โ Ajax-powered expense type search and management
- โ Real-time cash register balance validation
- โ Automatic accounting journal entry generation
- โ Cost center allocation and expense tracking
- โ Multi-source expense categorization (assets vs expenses)
- โ Third-party API integration for transportation expenses
- โ Employee/doctor expense assignment
- โ Permission-based expense access control
- โ Complex expense reversal with accounting cleanup
Related Controllers
- โข expensesController.php - Main expense processing
- โข dailyentryfun.php - Accounting journal entries
- โข saveController.php - Cash register management
- โข costcenterController.php - Cost center management
---
๐๏ธ Database Tables
Core Expense Tables
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **expensestype** | Expense category definitions | expensestypeid, expensestypename, saveid, withinsupervision_ratio | |
| **expenses** | Expense transactions | expensesid, expensesname, expensesValue, expensestypeid, dailyentryid | |
| **save** | Cash registers/safes | saveid, savename, savecurrentvalue, treeId | |
| **savedaily** | Cash register transaction log | savedailyid, saveid, savedailychangeamount, savedailychangetype |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **dailyentry** | Journal entry headers | id, totalcreditor, totaldebtor, entryComment, operationId | |
| **dailyentrycreditor** | Credit side entries | dailyentryid, accountstreeid, value, dComment | |
| **dailyentrydebtor** | Debit side entries | dailyentryid, accountstreeid, value, dComment | |
| **accountstree** | Chart of accounts | id, name, customName, treeId |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **costcenter** | Cost center definitions | id, name, description | |
| **costcenterdetail** | Cost allocations | costcenterid, costamount, modelid, tablename, type | |
| **assetscat** | Asset categories | id, name, description | |
| **assets** | Asset master data | assetId, assetsName | |
| **employeedoctor** | Employee/doctor records | id, name, thetype |
๐ Key Functions
1. add - Expense Type Creation (Ajax)
Location: Line 182
Purpose: Real-time expense type creation during expense entry
Process Flow:
1. Validate expense type name and details
2. Create new expensestype record
3. Link to accounts tree for accounting integration
4. Return updated expense type list for dropdown
2. update - Comprehensive Expense Update
Location: Line 427
Purpose: Complex expense modification with accounting reversal and recreation
Function Logic:
function update() {
global $ExpenseDAO, $dailyEntry, $CostcenterdetailEX;
// Get expense data
$expenseid = $_POST['expenseid'];
$expensevalue = $_POST['expensevalue'];
$expensetype = $_POST['expensetype'];
$dailyentryid = $_POST['dailyentryid'];
$oldData = $ExpenseDAO->load($expenseid);
// Handle condition changes (delete/restore)
if ($tempdele != $conditions) {
if ($conditions == 0) { // Restore expense
// Validate cash register balance
$saveValueBefore = getSaveValue();
if ($saveValueBefore >= $expensevalue) {
// Update cash register
updateSave($saveId, $saveValuebefore - $expensevalue);
// Create journal entries
createExpenseJournalEntry($expensevalue, $expensetype);
} else {
return "0"; // Insufficient funds
}
} elseif ($conditions == 1) { // Delete expense
// Reverse cash register transaction
updateSave($saveId, $saveValuebefore + $expensevalue);
// Update cost center details
$CostcenterdetailEX->updatedellbytypeandmodelid('6', $expenseid, 1);
}
}
// Reverse old journal entry
$x = reverseEntryWithItsID($dailyentryid);
// Create new journal entry with updated values
$returnedData = insertEntery($dailyEntry, $dailyEntryDebtorArray, $dailyEntryCreditorArray);
$newDailyEntryId = $returnedData[1];
// Update expense record
$ExpenseDAO->update($updatedExpense);
return "1"; // Success
}
3. expensetypesave - Ajax Expense Type Search
Location: Line 253
Purpose: Real-time search for expense types with cash register filtering
Function Logic:
function expensetypesave() {
global $ExpensetypeEX;
$searchTerm = $_POST['searchTerm'];
$saveid = $_POST['saveid'];
$queryString = " and expensestypename LIKE '%" . $searchTerm . "%' ";
if ($saveid) {
$queryString .= " and ( saveid = " . $saveid . " or saveid = 0 ) ";
}
$expensetypes = $ExpensetypeEX->expensetypesave($queryString);
$return_arr = array();
foreach ($expensetypes as $pro) {
$row_array = array();
$row_array['id'] = $pro->expensestypeid;
$row_array['text'] = $pro->expensestypename;
array_push($return_arr, $row_array);
}
echo json_encode($return_arr);
}
4. expensetypesaveuser - User-Restricted Expense Search
Location: Line 269
Purpose: Expense type search with user permission filtering
Permission Logic:
if ($_SESSION['searchinonesave'] == 0 && $_SESSION['saveids'] != 0) {
$queryString = ' and saveid in (' . $_SESSION['saveids'] . ') or saveid = 0 ';
}
foreach ($expensetypes as $pro) {
$row_array = array();
$row_array['id'] = $pro->expensestypeid;
$row_array['text'] = $pro->expensestypename;
$row_array['withinsupervisionratio'] = $pro->withinsupervision_ratio;
array_push($return_arr, $row_array);
}
---
๐ Third-Party API Integration
Africano Transportation System Integration
Location: Lines 290-370
Purpose: Integration with external transportation management system
5. triptype - Transportation Type Ajax
elseif ($do == "triptype") {
$return_arr = array();
$response = CURL_Request(array(), getenv('africanoUrl') . '/trip-type', 1, 0, getenv('africanoToken'));
$receivedata = json_decode($response);
foreach ($receivedata->data as $data) {
$row_array = array();
$row_array['id'] = $data->id;
$row_array['text'] = $data->name;
array_push($return_arr, $row_array);
}
echo json_encode($return_arr);
}
6. trips - Trip Management Ajax
elseif ($do == "trips") {
$response = CURL_Request(array(), getenv('africanoUrl') . '/trip', 1, 0, getenv('africanoToken'));
// Process trip data for dropdown population
}
7. dateid - Trip Date Filtering
elseif ($do == "dateid") {
$tripid = (int) $_REQUEST['tripid'];
$response = CURL_Request(array(), getenv('africanoUrl') . '/trip-date?trip_id=' . $tripid, 1, 0, getenv('africanoToken'));
foreach ($receivedata->data as $data) {
$row_array = array();
$row_array['id'] = $data->id;
$row_array['text'] = $data->trip_name . "/" . $data->day_date;
$row_array['trip_id'] = $data->trip_id;
array_push($return_arr, $row_array);
}
}
API Endpoints Integrated:
- โข
/trip-type- Transportation type definitions - โข
/trip- Available trips - โข
/trip-date- Trip scheduling dates - โข
/path- Route definitions - โข
/referrer- Referral sources - โข
/driver- Driver information - โข
/bus- Vehicle information
---
๐ Utility Functions
8. expenseTypeAjax - Dynamic Expense Type Loading
Location: Line 717
Purpose: Load expense types based on user permissions and search criteria
function expenseTypeAjax() {
global $ExpensetypeEX, $userData;
$name = $_GET['term'];
$limit = 15;
if ($userData->searchinonesave == 0) {
$allParents = $ExpensetypeEX->queryAllExtNotParent();
} else {
$allParents = $ExpensetypeEX->queryAllWithSaveNotParent($_SESSION['saveid']);
}
foreach ($allParents as $value) {
if ($value->parent > 0) {
$value->expensestypename = "$value->parentexpensestypename / $value->expensestypename";
}
$row_array['id'] = $value->expensestypeid;
$row_array['text'] = $value->expensestypename;
array_push($return_arr, $row_array);
}
echo json_encode($return_arr);
}
9. Cash Register Management Functions
getSaveValue() - Get Current Cash Balance:
function getSaveValue() {
global $SaveDAO;
$saveData = $SaveDAO->load($_SESSION['saveid']);
return $saveData->savecurrentvalue;
}
updateSave() - Update Cash Register:
function updateSave($saveid, $savevalueafter) {
global $Save, $SaveExt;
$Save->savecurrentvalue = $savevalueafter;
$Save->userid = $_SESSION['userid'];
$Save->saveid = $saveid;
$SaveExt->updateSaveValue($Save);
}
insertSavedaily() - Log Cash Transactions:
function insertSavedaily($savedailysavebefore, $savedailychangeamount, $savedailychangetype,
$saveid, $processname, $savedailymodelid, $savedailysaveafter, $tablename) {
global $Savedaily, $SavedailyDAO;
$Savedaily->savedailydate = date("Y-m-d H:i:s");
$Savedaily->userid = $_SESSION['userid'];
$Savedaily->savedailysavebefore = $savedailysavebefore;
$Savedaily->savedailychangeamount = $savedailychangeamount;
$Savedaily->savedailychangetype = $savedailychangetype;
$Savedaily->saveid = $saveid;
$Savedaily->processname = $processname;
$Savedaily->savedailymodelid = $savedailymodelid;
$Savedaily->savedailysaveafter = $savedailysaveafter;
$Savedaily->tablename = $tablename;
$SavedailyDAO->insert($Savedaily);
}
---
๐ Workflows
Workflow 1: Complete Expense Processing with Accounting Integration
---
Workflow 2: Expense Update with Accounting Reversal
---
๐ Ajax API Endpoints
| Endpoint | Method | Purpose | Parameters | Response | |
|---|---|---|---|---|---|
| `?do=add` | POST | Create new expense type | `expenseName`, `expensecomment` | HTML template | |
| `?do=edit` | GET | Load expense for editing | `id` | HTML template | |
| `?do=update` | POST | Update expense | `expenseid`, `expensevalue`, `expensetype`, etc. | Status code | |
| `?do=isParent` | GET | Check if expense type has children | `id` | "isParent" or "isNotParent" | |
| `?do=hasExpenses` | GET | Check if type has expenses | `id` | "yes" or "no" | |
| `?do=checkExp` | POST | Validate expense name uniqueness | `expensesname` | -1 or 0 | |
| `?do=addCat` | POST | Add expense category | `name`, `descripe`, `parent` | Redirect | |
| `?do=expenseTypeAjax` | GET | Get expense types for dropdown | `assetorexpense` | JSON array | |
| `?do=getemployeeOrDoctorajax` | GET | Get employee/doctor list | `employeeOrDoctor` | JSON array | |
| `?do=expensetypesave` | POST | Search expense types | `searchTerm`, `saveid` | JSON array | |
| `?do=expensetypesaveuser` | POST | User-restricted expense search | `searchTerm`, `saveid` | JSON array |
| Endpoint | Purpose | External API | |
|---|---|---|---|
| `?do=triptype` | Transportation types | `/trip-type` | |
| `?do=trips` | Available trips | `/trip` | |
| `?do=paths` | Route definitions | `/path` | |
| `?do=dateid` | Trip dates | `/trip-date` | |
| `?do=referrer` | Referral sources | `/referrer` | |
| `?do=driver` | Driver information | `/driver` | |
| `?do=bus` | Vehicle information | `/bus` |
๐ Security & Permissions
User Permission Controls
// Expense type filtering based on user permissions
if ($_SESSION['searchinonesave'] == 0 && $_SESSION['saveids'] != 0) {
$queryString = ' and saveid in (' . $_SESSION['saveids'] . ') or saveid = 0 ';
}
// User can only see expense types for their assigned cash registers
$userData = $myUserRecord->load($_SESSION['userid']);
if ($userData->searchinonesave == 0) {
$allParents = $ExpensetypeEX->queryAllExtNotParent();
} else {
$allParents = $ExpensetypeEX->queryAllWithSaveNotParent($_SESSION['saveid']);
}
Input Validation
// Validate expense amounts
$expensevalue = (float) filter_input(INPUT_POST, 'expensevalue');
if ($expensevalue <= 0) {
return "0"; // Invalid amount
}
// Validate expense type exists
$expenseTypeData = $ExpensetypeDAO->load($expensetype);
if (empty($expenseTypeData)) {
return "0"; // Invalid expense type
}
Cash Register Protection
// Prevent negative cash balances
$currentBalance = getSaveValue();
if ($currentBalance < $expenseAmount) {
return "0"; // Insufficient funds
}
---
๐ Performance Considerations
Ajax Optimization
1. Efficient Search Queries:
- Use LIKE queries with proper indexing
- Limit results to prevent large response sizes
- Cache frequently accessed expense types
2. Database Indexes Required:
CREATE INDEX idx_expensestype_name ON expensestype(expensestypename, saveid);
CREATE INDEX idx_expenses_type ON expenses(expensestypeid, conditions);
CREATE INDEX idx_savedaily_save ON savedaily(saveid, savedailydate);
```
### Third-Party API Performance
php
// Add timeout and error handling for external APIs
$response = CURL_Request(array(), $url, 1, 0, $token, [
'timeout' => 10,
'connecttimeout' => 5,
'retry_count' => 2
]);
if ($response === false) {
// Return empty array or cached data
echo json_encode([]);
exit;
}
---
## ๐ Common Issues & Troubleshooting
### 1. **Ajax Search Not Working**
**Issue**: Expense type search returns no results
**Cause**: Permission filtering or empty search parameters
**Debug**:
php
// Log search parameters
error_log("Search term: " . $_POST['searchTerm']);
error_log("Save ID: " . $_POST['saveid']);
error_log("User save restriction: " . $_SESSION['searchinonesave']);
error_log("User save IDs: " . $_SESSION['saveids']);
### 2. **Insufficient Funds Error**
**Issue**: Cannot create expense despite having cash
**Cause**: Cash register balance calculation error
**Debug**:
php
$currentBalance = getSaveValue();
$expenseAmount = $_POST['expensevalue'];
error_log("Current balance: $currentBalance");
error_log("Expense amount: $expenseAmount");
error_log("Difference: " . ($currentBalance - $expenseAmount));
### 3. **Journal Entry Imbalance**
**Issue**: Accounting entries don't balance
**Cause**: Currency conversion or discount calculation errors
**Debug**:
php
$totalDebits = getTotal($dailyEntryDebtorArray);
$totalCredits = getTotal($dailyEntryCreditorArray);
error_log("Debits: $totalDebits, Credits: $totalCredits");
### 4. **Third-Party API Failures**
**Issue**: External API calls timeout or return errors
**Cause**: Network issues or API key problems
**Debug**:
php
error_log("API URL: " . getenv('africanoUrl'));
error_log("API Token: " . substr(getenv('africanoToken'), 0, 10) . "...");
error_log("API Response: " . $response);
---
## ๐งช Testing Scenarios
### Test Case 1: Basic Expense Creation
1. Select expense type via Ajax search
2. Enter valid expense amount
3. Verify cash register balance check
4. Submit expense form
5. Verify journal entry creation
6. Check cash register balance update
7. Confirm expense appears in expense list
### Test Case 2: Expense Update with Reversal
1. Edit existing expense
2. Change amount and/or type
3. Verify original journal entry reversal
4. Verify new journal entry creation
5. Check cash register adjustment
6. Confirm updated expense data
### Test Case 3: Permission-Based Search
1. Login as user with restricted save access
2. Search for expense types
3. Verify only permitted types returned
4. Login as admin user
5. Verify all types available
### Test Case 4: Third-Party API Integration
1. Test each Africano API endpoint
2. Verify JSON response structure
3. Test with invalid API credentials
4. Test network timeout handling
5. Verify graceful degradation
```
---
๐ Related Documentation
- โข CLAUDE.md - PHP 8.2 migration guide
- โข dailyentryfun.md - Accounting journal functions
- โข expensesController.php - Main expense processing
- โข Ajax Best Practices Guide - Frontend integration patterns
---
Documented By: AI Assistant
Review Status: โ Complete
Next Review: When expense processing logic changes