Checkwithdrawal Documentation
Check Withdrawal Controller Documentation
File: /controllers/checkwithdrawalController.php
Purpose: Manages check withdrawals for supplier payments with comprehensive financial tracking
Last Updated: December 20, 2024
Total Functions: 10
Lines of Code: ~1,027
---
๐ Overview
The Check Withdrawal Controller is a sophisticated financial management module that handles check payments to suppliers with comprehensive account tracking and debt management. It handles:
- โข Check withdrawal creation and processing
- โข Bank account balance updates
- โข Supplier debt reduction tracking
- โข Account movement recording
- โข Daily entry generation for accounting
- โข Multi-criteria search and filtering
- โข Bulk operations management
- โข Complete audit trail maintenance
Primary Functions
- โ Create check withdrawals for supplier payments
- โ Update bank account balances automatically
- โ Track supplier debt changes
- โ Record account movements
- โ Generate accounting daily entries
- โ Search by date range, supplier, or check ID
- โ Edit check details (user and notes only)
- โ Delete/cancel check withdrawals with reversal
- โ Bulk operations on multiple checks
- โ Supplier concurrency management
Financial Impact
When Check Withdrawn:
1. Bank Account: Decreased by check amount
2. Supplier Debt: Decreased by check amount
3. Account Movement: Withdrawal transaction recorded
4. Daily Entry: Debit supplier account, credit bank account
When Check Cancelled:
1. Bank Account: Increased by check amount (reversed)
2. Supplier Debt: Increased by check amount (reversed)
3. Account Movement: Deposit transaction recorded
4. Daily Entry: Reversed automatically
Related Controllers
- โข cashSaveFlowController.php - Cash flow reporting
- โข supplierPayedDeptController.php - Other supplier payments
- โข bankaccountController.php - Bank account management
- โข supplierController.php - Supplier management
---
๐๏ธ Database Tables
Primary Tables (Direct Operations)
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **checkwithdrawal** | Check withdrawal records | checkwithdrawalid, bankaccountid, supplierid, checkwithdrawalamount, checkwithdrawalnumber, conditions, dailyentryid | |
| **accountmovement** | Bank account movements | accountmovementid, accountid, accountmovementamount, accountmovementtype, tablename, accountmovementmodelid | |
| **supplierdebtchange** | Supplier debt tracking | supplierdebtchangeid, supplierid, supplierdebtchangeamount, supplierdebtchangetype, tablename, supplierdebtchangemodelid |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **bankaccount** | Bank account master | accountid, accountname, accountbeginingbalance, bankid, treeId | |
| **bank** | Bank information | bankid, bankname | |
| **supplier** | Supplier master data | supplierid, suppliername, suppliercurrentDebt, treeId, inUse | |
| **user** | System users | userid, username |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **dailyentry** | Journal entry header | dailyentryid, entryComment, userid | |
| **dailyentrycreditor** | Credit side entries | dailyentrycreditorid, dailyentryid, accountstreeid, value | |
| **dailyentrydebtor** | Debit side entries | dailyentrydebttorid, dailyentryid, accountstreeid, value | |
| **accountstree** | Chart of accounts | accountstreeid, accountname |
๐ Key Functions
1. Default Action - Add Check Form
Location: Lines 141-164
Purpose: Display form to create new check withdrawal
Process Flow:
1. Load all banks with extended information
2. Load all suppliers for selection
3. Display add form template
4. Set custom validation flag
Template Variables:
- โข
$bank- Available banks list - โข
$allsuplierdata- Available suppliers list - โข
$customdepositcheck = 1- Enable custom validation
---
2. add - Create Check Withdrawal
Location: Lines 401-624
Purpose: Process new check withdrawal with full financial impact
Function Signature:
function add()
Process Flow:
Phase 1: Input Validation and Setup
$supplierid = $_POST["supplierid"];
$accountid = (int) $_POST["accountid"];
$checkwithdrawalamount = $_POST["txtAmount"];
$checkwithdrawalnumber = $_POST["txtcheckNum"];
$checkwithdrawaluser = $_POST["txtUser"];
$checkwithdrawalnote = $_POST["textNote"];
Phase 2: Check Withdrawal Record Creation
$checkWithdrawal->bankaccountid = $accountid;
$checkWithdrawal->supplierid = $supplierid;
$checkWithdrawal->accountbeforevalue = $accountbeginingbalance;
$checkWithdrawal->checkwithdrawalamount = $checkwithdrawalamount;
$checkWithdrawal->checkwithdrawaldate = date("Y-m-d H:i:s");
$checkWithdrawal->conditions = 0; // Active
$checkWithdrawal->userid = $_SESSION['userid'];
$checkWithdrawalId = $checkWithdrawalDAO->insert($checkWithdrawal, $checkwithdrawalnote);
Phase 3: Supplier Debt Management
// Load supplier with concurrency control
$supplierdata = getSupplierDataFromSupplierInUseSP($supplierid);
$debtBefore = $supplierdata->suppliercurrentDebt;
// Update supplier debt (decrease by check amount)
$Supplier->suppliercurrentDebt = ($debtBefore - $checkwithdrawalamount);
$SupplierEX->updatedept($Supplier);
// Record debt change
$supDebtChange->supplierdebtchangebefore = $debtBefore;
$supDebtChange->supplierid = $supplierid;
$supDebtChange->supplierdebtchangeamount = $checkwithdrawalamount;
$supDebtChange->supplierdebtchangetype = 1; // Decrease
$supDebtChange->tablename = "checkwithdrawalController.php";
$supDebtChange->supplierdebtchangemodelid = $checkWithdrawalId;
$supDebtChange->supplierdebtchangeafter = $debtBefore - $checkwithdrawalamount;
$supDebtChange->processname = "ุฏูุน ุดูู"; // "Check payment"
$supDebtChangeDAO->insert($supDebtChange);
Phase 4: Bank Account Update
$accoundatat = $accountDAO->load($accountid);
$accountBefore = $accoundatat->accountbeginingbalance;
// Update account balance (decrease by check amount)
$account->accountbeginingbalance = $accountBefore - $checkwithdrawalamount;
$accountExtDAO->updateacount($account);
// Record account movement
$accountMove->accountmovementbefore = $accountBefore;
$accountMove->accountid = $accountid;
$accountMove->accountmovementamount = $checkwithdrawalamount;
$accountMove->accountmovementafter = $accountBefore - $checkwithdrawalamount;
$accountMove->accountmovementtype = 1; // Withdrawal
$accountMove->processname = "ุณุญุจ ุดูู"; // "Check withdrawal"
$accountMove->tablename = "checkwithdrawalController.php";
$accountMove->accountmovementmodelid = $checkWithdrawalId;
$accountMoveDAO->insert($accountMove);
Phase 5: Daily Entry Generation
$dailyEntry->entryComment = 'ุชู
ุนู
ู ุณุญุจ ุดูู ููู
ูุฑุฏ ( ' . $supplierdata->suppliername . ' )';
// Debit: Supplier account (debt reduction)
$dailyEntryDebtor->value = $checkwithdrawalamount;
$dailyEntryDebtor->accountstreeid = $supplierdata->treeId;
// Credit: Bank account
$dailyEntryCreditor->value = $checkwithdrawalamount;
$accountData = $accountDAO->load($accountid);
$dailyEntryCreditor->accountstreeid = $accountData->treeId;
$returnedData = insertEntery($dailyEntry, $dailyEntryDebtorArray, $dailyEntryCreditorArray, 1, $checkWithdrawalId, "checkwithdrawalController.php?do=show&id=$checkWithdrawalId");
Error Handling:
- โข Transaction rollback on any failure
- โข Supplier concurrency lock release
- โข Validation of account balances
Return Values:
- โข
"0"- Success - โข
"1"- Error (insufficient funds or validation failure)
---
3. show - Display Check List
Location: Lines 627-690
Purpose: Search and display check withdrawals based on criteria
Function Signature:
function show()
Search Criteria:
Date Range Search:
$from = $_REQUEST["from"];
$to = $_REQUEST["to"];
if (isset($to) && $to != "" && isset($from) && $from != "") {
$message = "ุนุฑุถ ุงูู
ุณุญูุจุงุช ู
ู ุชุงุฑูุฎ : " . $from . " ุงูู ุชุงุฑูุฎ " . $to;
$shownData = $checkWithdrawalExtDAO->queryBydateExt($from, $to);
}
Supplier Filter:
$supplierid = $_REQUEST["supplierid"];
if (isset($supplierid) && $supplierid != "-1") {
$mysupplierdata = $SupplierDAO->load($supplierid);
$message = "ุนุฑุถ ุงูู
ุณุญูุจุงุช ููู
ูุฑุฏ : " . $mysupplierdata->suppliername;
$shownData = $checkWithdrawalExtDAO->queryWithSupplieridExt($supplierid);
}
Specific Check ID:
$id = (int) $_REQUEST["id"];
if (isset($id) && $id > 0) {
$message = "ุนุฑุถ ุงูู
ุณุญูุจุงุช ุฑูู
: " . $id;
$temp = $checkWithdrawalExtDAO->loadExt($id);
$shownData = array($temp);
}
Default (Today's Checks):
else {
$message = "ุนุฑุถ ุงูู
ุณุญูุจุงุช ู
ู ุชุงุฑูุฎ : " . date("Y-m-d") . " ุงูู ุชุงุฑูุฎ : " . date("Y-m-d");
$shownData = $checkWithdrawalExtDAO->queryBydateExt(date("Y-m-d"), date("Y-m-d"));
}
---
4. delete - Cancel Check Withdrawal
Location: Lines 694-850
Purpose: Reverse check withdrawal with complete financial reversal
Function Signature:
function delete($id)
Process Flow:
Phase 1: Load Check Data
$checkdata = $checkWithdrawalDAO->load($checkid);
$accountid = $checkdata->bankaccountid;
$checkwithdrawalamount = $checkdata->checkwithdrawalamount;
$supplierid = $checkdata->supplierid;
Phase 2: Reverse Supplier Debt
$supplierdata = getSupplierDataFromSupplierInUseSP($supplierid);
$debtBefore = $supplierdata->suppliercurrentDebt;
// Increase supplier debt (reverse the payment)
$Supplier->suppliercurrentDebt = ($debtBefore + $checkwithdrawalamount);
$SupplierEX->updatedept($Supplier);
// Record reversal in debt change
$supDebtChange->supplierdebtchangebefore = $debtBefore;
$supDebtChange->supplierdebtchangeamount = $checkwithdrawalamount;
$supDebtChange->supplierdebtchangetype = 0; // Increase
$supDebtChange->tablename = "checkwithdrawalController.php";
$supDebtChange->supplierdebtchangeafter = $debtBefore + $checkwithdrawalamount;
$supDebtChange->processname = "ุญุฐู ุฏูุน ุดูู"; // "Delete check payment"
$supDebtChangeDAO->insert($supDebtChange);
Phase 3: Mark Check as Deleted
$checkWithdrawal->conditions = 1; // Deleted
$checkWithdrawal->checkwithdrawalid = $checkid;
$checkWithdrawalExtDAO->updatedel($checkWithdrawal);
Phase 4: Reverse Bank Account
$accountdata = $accountDAO->load($accountid);
$accountBefore = $accountdata->accountbeginingbalance;
// Increase account balance (reverse the withdrawal)
$account->accountbeginingbalance = $accountBefore + $checkwithdrawalamount;
$accountExtDAO->updateacount($account);
// Record reversal movement
$accountMove->accountmovementbefore = $accountBefore;
$accountMove->accountmovementamount = $checkwithdrawalamount;
$accountMove->accountmovementafter = $accountBefore + $checkwithdrawalamount;
$accountMove->accountmovementtype = 0; // Deposit
$accountMove->processname = "ุงูุบุงุก ุณุญุจ ุดูู"; // "Cancel check withdrawal"
$accountMove->tablename = "wirhdrawalCheckController.php";
$accountMoveDAO->insert($accountMove);
Phase 5: Reverse Daily Entry
// Reverse the original daily entry
if (isset($dailyentry) && $dailyentryid2 == '') {
reverseEntryWithItsID($dailyentry, 1);
} else {
reverseEntryWithItsID($dailyentryid2, 1);
}
Return Values:
- โข
"sucess"- Successful cancellation - โข Error message string - If cancellation failed
---
5. edit - Load Check for Editing
Location: Lines 853-879
Purpose: Load check data with debt information for editing
Function Signature:
function edit()
Process Flow:
1. Load check withdrawal with supplier information
2. Load related debt change information
3. Calculate debt before/after values
4. Return data object for form population
Debt Information Loading:
$showData = $checkWithdrawalExtDAO->queryByIdExtwithsuplier($id);
if ($showData->suppliername) {
$debtRow = R::getRow('select supplierdebtchangebefore,supplierdebtchangeafter from supplierdebtchange where tablename="checkwithdrawalController.php" and supplierdebtchangemodelid=' . $id);
$debtBefore = $debtRow['supplierdebtchangebefore'];
$debtAfter = $debtRow['supplierdebtchangeafter'];
}
---
6. update - Update Check Details
Location: Lines 882-902
Purpose: Update only user and note fields (amount cannot be changed)
Function Signature:
function update()
Updateable Fields:
- โข
checkwithdrawaluser- Check recipient name - โข
checkwithdrawalnote- Additional notes
Restriction: Check amount, supplier, and bank account cannot be changed after creation.
---
7. execute - Bulk Operations
Location: Lines 927-984
Purpose: Perform bulk operations on multiple selected checks
Function Signature:
function execute()
Process Flow:
1. Get operation type and selected check IDs
2. For each selected check:
- Validate operation (only active checks can be deleted)
- Execute delete operation
- Record success/failure messages
3. Display results summary
Operation Types:
- โข
1- Bulk delete/cancel checks
Validation:
if ($operationType == '1' && $conditions == 0) { // Can delete only active checks
$note = delete($checkId);
if ($note != "sucess") {
$outputString .= $checkwithdrawalnumber . ": " . $note . "<br/>";
} else {
$outputString .= $checkwithdrawalnumber . ": ุชู
ุช ุงูุนู
ููุฉ ุจูุฌุงุญ <br/>";
}
} else {
$outputString .= $checkwithdrawalnumber . ": ูุง ูู
ูู ุงูุบุงุก ูุฐุง ุงูุดูู ูุงูู ู
ูุบู ุณุงุจูุง<br/>";
}
---
8. getSupplierDataFromSupplierInUseSP - Concurrency Management
Location: Lines 986-1007
Purpose: Load supplier data with concurrency control to prevent debt conflicts
Function Signature:
function getSupplierDataFromSupplierInUseSP($supplier)
Concurrency Logic:
$supplier_data = $SupplierEX->callSupplierInUseSP($supplier);
while ($supplier_data->suppliercurrentDebt == 'in_use') {
sleep(1); // Wait 1 second
$noOfTries++;
if ($noOfTries > 15) { // Max 15 seconds wait
// Force unlock supplier
R::exec('UPDATE supplier SET inUse = 0 where supplierid = ' . $supplier);
}
$supplier_data = $SupplierEX->callSupplierInUseSP($supplier);
}
Purpose: Prevents multiple users from simultaneously modifying the same supplier's debt, which could lead to data corruption.
---
๐ Workflows
Workflow 1: Check Withdrawal Creation
---
Workflow 2: Check Withdrawal Cancellation
---
๐ URL Routes & Actions
| URL Parameter | Function Called | Description | |
|---|---|---|---|
| (no parameters) | Default action | Display add check form | |
| `do=add` | `add()` | Process new check withdrawal | |
| `do=show` | `show()` | Display check list with filters | |
| `do=delete` | `delete()` | Cancel/delete check withdrawal | |
| `do=edit` | `edit()` | Load check data for editing | |
| `do=update` | `update()` | Update check user/notes only | |
| `do=details` | `edit()` | Display check details (read-only) | |
| `do=editprint` | `edit()` | Display check in print format | |
| `do=executeOperation` | `execute()` | Bulk operations on selected checks | |
| `do=sucess` | Success page | Display success message | |
| `do=error` | Error page | Display error message |
Create Check (do=add):
- โข
supplierid- Supplier ID - โข
accountid- Bank account ID - โข
txtAmount- Check amount - โข
txtcheckNum- Check number - โข
txtUser- Check recipient name - โข
textNote- Optional notes
Show Checks (do=show):
- โข
from- Start date (optional) - โข
to- End date (optional) - โข
supplierid- Supplier filter (optional) - โข
id- Specific check ID (optional)
Delete Check (do=delete):
- โข
id- Check withdrawal ID - โข
action- Daily entry ID (optional) - โข
dailyentryid- Daily entry ID (POST, optional)
Edit Check (do=edit):
- โข
id- Check withdrawal ID
Update Check (do=update):
- โข
checkwithdrawalid- Check ID - โข
txtUser- Updated recipient name - โข
textNote- Updated notes
Bulk Operations (do=executeOperation):
- โข
operation- Operation type (1=delete) - โข
choosedItem[]- Array of check IDs - โข
dailyentryid- Daily entry ID for reversals
---
๐งฎ Financial Calculations
Check Withdrawal Impact
// Bank Account Balance
$newAccountBalance = $currentAccountBalance - $checkAmount;
// Supplier Debt
$newSupplierDebt = $currentSupplierDebt - $checkAmount;
// Account Movement
$accountMovement = [
'before' => $currentAccountBalance,
'amount' => $checkAmount,
'after' => $newAccountBalance,
'type' => 1 // Withdrawal
];
// Supplier Debt Change
$supplierDebtChange = [
'before' => $currentSupplierDebt,
'amount' => $checkAmount,
'after' => $newSupplierDebt,
'type' => 1 // Decrease
];
Check Cancellation Impact
// Bank Account Balance (Reversal)
$newAccountBalance = $currentAccountBalance + $checkAmount;
// Supplier Debt (Reversal)
$newSupplierDebt = $currentSupplierDebt + $checkAmount;
// Account Movement (Reversal)
$accountMovement = [
'before' => $currentAccountBalance,
'amount' => $checkAmount,
'after' => $newAccountBalance,
'type' => 0 // Deposit
];
// Supplier Debt Change (Reversal)
$supplierDebtChange = [
'before' => $currentSupplierDebt,
'amount' => $checkAmount,
'after' => $newSupplierDebt,
'type' => 0 // Increase
];
Daily Entry Structure
// Debit Side: Supplier Account (Accounts Payable decrease)
$debitEntry = [
'accountstreeid' => $supplier->treeId,
'value' => $checkAmount,
'description' => 'Check payment to supplier'
];
// Credit Side: Bank Account (Cash decrease)
$creditEntry = [
'accountstreeid' => $bankAccount->treeId,
'value' => $checkAmount,
'description' => 'Check withdrawal from bank'
];
---
๐ Security & Permissions
Access Control
// All actions require authentication
include_once("../public/authentication.php");
// User session tracking
$checkWithdrawal->userid = $_SESSION['userid'];
Input Validation
// Type casting for critical fields
$accountid = (int) $_POST["accountid"];
$id = (int) $_REQUEST["id"];
// SQL injection prevention through DAO layer
// All database operations use parameterized queries
Concurrency Control
// Supplier locking mechanism
function getSupplierDataFromSupplierInUseSP($supplier) {
$supplier_data = $SupplierEX->callSupplierInUseSP($supplier);
while ($supplier_data->suppliercurrentDebt == 'in_use') {
sleep(1); // Wait for lock release
$noOfTries++;
if ($noOfTries > 15) {
// Force unlock after 15 seconds
R::exec('UPDATE supplier SET inUse = 0 where supplierid = ' . $supplier);
}
$supplier_data = $SupplierEX->callSupplierInUseSP($supplier);
}
return $supplier_data;
}
Transaction Integrity
// Database transactions ensure consistency
$mytransactions = new Transaction();
try {
// All operations
$mytransactions->commit();
} catch (Exception $ex) {
$mytransactions->rollback();
markSupplierAsNOTInUse($supplierid);
}
---
๐ Performance Considerations
Database Optimization Tips
1. Critical Indexes:
- checkwithdrawal(bankaccountid, supplierid) - For account/supplier searches
- checkwithdrawal(checkwithdrawaldate) - For date range queries
- supplierdebtchange(tablename, supplierdebtchangemodelid) - For linking
2. Query Optimization:
- Date range queries could benefit from partitioning
- Supplier concurrency could use database-level locking
- Extended DAO queries load multiple related tables
3. Concurrency Issues:
- Sleep-based waiting is inefficient
- Could implement proper database locking
- Supplier "inUse" flag needs timeout mechanism
Performance Bottlenecks
// This pattern appears frequently and could be optimized:
while ($supplier_data->suppliercurrentDebt == 'in_use') {
sleep(1); // Inefficient - blocks thread
$noOfTries++;
if ($noOfTries > 15) {
// Force unlock - could cause data inconsistency
R::exec('UPDATE supplier SET inUse = 0 where supplierid = ' . $supplier);
}
}
Better Approach:
-- Use database-level locking instead
SELECT ... FROM supplier WHERE supplierid = ? FOR UPDATE;
---
๐ Common Issues & Troubleshooting
1. Check Creation Fails with Insufficient Balance
Issue: Check withdrawal fails even when account has sufficient funds
Cause: Concurrency issues or stale balance data
Debug:
-- Check current account balance
SELECT accountbeginingbalance FROM bankaccount WHERE accountid = [ACCOUNT_ID];
-- Check recent movements
SELECT * FROM accountmovement
WHERE accountid = [ACCOUNT_ID]
ORDER BY accountmovementdate DESC
LIMIT 10;
2. Supplier Debt Not Updating
Issue: Supplier debt doesn't decrease after check withdrawal
Cause: Concurrency lock issues or transaction rollback
Debug:
-- Check supplier lock status
SELECT supplierid, suppliercurrentDebt, inUse FROM supplier WHERE supplierid = [SUPPLIER_ID];
-- Check recent debt changes
SELECT * FROM supplierdebtchange
WHERE supplierid = [SUPPLIER_ID]
ORDER BY supplierdebtchangedate DESC
LIMIT 5;
3. Daily Entry Not Generated
Issue: Accounting entry missing for check withdrawal
Cause: Missing tree IDs or daily entry function failure
Debug:
-- Check tree ID assignments
SELECT s.supplierid, s.suppliername, s.treeId as supplier_tree,
ba.accountid, ba.accountname, ba.treeId as account_tree
FROM supplier s, bankaccount ba
WHERE s.supplierid = [SUPPLIER_ID] AND ba.accountid = [ACCOUNT_ID];
-- Check if daily entry was created
SELECT * FROM dailyentry WHERE tablename = 'checkwithdrawalController.php'
ORDER BY dailyentrydate DESC LIMIT 5;
4. Concurrency Timeout Issues
Issue: Multiple users cannot work with same supplier simultaneously
Cause: Supplier locking mechanism too aggressive
Solutions:
- โข Reduce timeout from 15 to 5 seconds
- โข Implement proper database locking
- โข Add user notification for locked suppliers
---
๐งช Testing Scenarios
Test Case 1: Normal Check Withdrawal
1. Select supplier with sufficient debt
2. Select bank account with sufficient funds
3. Create check withdrawal
4. Verify all tables updated correctly:
- checkwithdrawal record created
- bankaccount balance decreased
- supplier debt decreased
- accountmovement recorded
- supplierdebtchange recorded
- dailyentry generated
Test Case 2: Check Cancellation
1. Create check withdrawal
2. Cancel the check
3. Verify all reversals work:
- Check marked as deleted (conditions = 1)
- Bank account balance restored
- Supplier debt restored
- Reversal movements recorded
- Daily entry reversed
Test Case 3: Concurrent Access
1. Two users try to create checks for same supplier
2. Verify concurrency control works
3. Check that only one succeeds initially
4. Verify second user can proceed after first completes
Test Case 4: Bulk Operations
1. Create multiple check withdrawals
2. Select subset for bulk cancellation
3. Verify all selected checks are cancelled
4. Verify unselected checks remain active
5. Check output messages for success/failure
---
๐ Related Documentation
- โข CLAUDE.md - PHP 8.2 migration guide
- โข cashSaveFlowController.md - Cash flow reporting
- โข supplierPayedDeptController.php - Other supplier payments
- โข Daily Entry System Documentation - Accounting integration
---
Documented By: AI Assistant
Review Status: โ Complete
Next Review: When major changes occur