Sellbill Documentation
Sell Bill Controller Documentation
File: /controllers/sellbillController.php
Purpose: Manages sales orders, sell bills, sales returns, and customer transactions
Last Updated: December 19, 2024
Total Functions: 95+
Lines of Code: ~14,765
---
๐ Overview
The Sell Bill Controller is the core component for managing all sales-related operations in the ERP system. It handles:
- โข Creating and editing sales bills (sell bills)
- โข Managing sales returns (return sell bills)
- โข Combined sell & return operations
- โข Sales offers/quotations
- โข Inventory stock decreases
- โข Customer debt tracking
- โข Daily accounting entries for sales
- โข Serial number tracking for sold items
- โข Restaurant/POS orders
- โข Online store integration
- โข OBGY (Obstetrics/Gynecology) visit billing
Primary Functions
- โ Create new sales bills
- โ Edit existing sales bills
- โ Delete sales bills
- โ View sales bill details
- โ Print sales bills
- โ Manage sales returns
- โ Track serial numbers
- โ Generate accounting entries
- โ Update inventory quantities
- โ Process customer payments
- โ Handle multi-payment methods (cash, visa, credit)
- โ Restaurant/kitchen orders
- โ Online order processing
- โ Excel/PDF export
Related Controllers
- โข buyBillController.php - Purchase operations
- โข clientController.php - Customer management
- โข productController.php - Product management
- โข storeController.php - Warehouse/store management
- โข returnSellBillController.php - Dedicated sales returns
- โข storedetailController.php - Inventory management
- โข dailyentryController.php - Accounting entries
- โข clientController.php - Customer management
- โข storedetailController.php - Inventory management
- โข dailyentryController.php - Accounting entries
---
๐๏ธ Database Tables
Primary Tables (Direct Operations)
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **sellbill** | Main sales bills | sellbillid, sellbillserial, sellbilldate, sellbilltotalbill, sellbillclientid, sellbillstoreid, sellbillsaveid | |
| **sellbilldetail** | Sales bill line items | sellbilldetailid, sellbilldetailproductid, sellbilldetailproductquantity, sellbilldetailproductprice, sellbillid | |
| **returnsellbill** | Sales return bills | returnsellbillid, returnsellbillserial, returnsellbilldate, returnsellbilltotalbill, returnsellbillclientid | |
| **returnsellbilldetail** | Return bill line items | returnsellbilldetailid, returnsellbilldetailproductid, returnsellbilldetailproductquantity, returnsellbillid | |
| **sellbillandrutern** | Combined sell & return bills | sellbillandruternid, sellbillandruternserial, sellbillandruterndate, sellbillandruternclientid | |
| **sellandruternbilldetail** | Combined bill details | sellandruternbilldetailid, sellandruternbilldetailproductid, sellandruternbilldetailproductquantity |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **storedetail** | Current stock levels | storedetailid, storeid, productid, productquantity | |
| **storereport** | Stock movement history | storereportid, productid, storeid, storereporttype, storereportmodelid | |
| **sizecolorstoredetail** | Stock by size/color | sizecolorstoredetailid, storeid, productid, sizeid, colorid, quantity | |
| **productserial** | Serial number master | productserailid, serialnumber, productid, don (sold flag) | |
| **soldserialproduct** | Sold serial tracking | soldserialproductid, productserailid, soldquantity, sellbillid, clientid | |
| **buypricesh istorybook** | FIFO/LIFO cost tracking | buypricesh istorybookid, productid, storeid, quantity, buyprice |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **client** | Customer accounts | clientid, clientname, clientdebt (running balance) | |
| **clientdebtchange** | Customer payment history | clientdebtchangeid, clientid, clientdebtchangeamount, clientdebtchangetype | |
| **save** | Cash registers/safes | saveid, savename, savevalue (current balance) | |
| **savedaily** | Cash register movements | savedailyid, saveid, savedailychangeamount, savedailychangetype | |
| **dailyentry** | Journal entries | dailyentryid, dailyentrymodelid, tablename, sysdate | |
| **dailyentrydebtor** | Debit entries | dailyentrydebtorid, dailyentryid, accountid, value | |
| **dailyentrycreditor** | Credit entries | dailyentrycreditorid, dailyentryid, accountid, value | |
| **bankaccountmovement** | Bank transactions | bankaccountmovementid, accountid, bankid, amount, type |
| Table Name | Purpose | Key Columns | |
|---|---|---|---|
| **product** | Product master | productid, productname, productcatid | |
| **billname** | Bill templates | billnameid, billname, billtype | |
| **billsettings** | Bill configuration | billsettingsid, billnameid, settings (JSON) | |
| **user** | System users | userid, username | |
| **programsettings** | System settings | programsettingsid, key, value |
๐ Key Functions
1. add() - Create New Sales Bill
Location: Line 2579
Purpose: Main function to create a new sales bill (regular/restaurant/quotation)
Function Signature:
function add()
Process Flow:
1. Begin transaction
2. Retrieve bill settings (billnameid, billtype)
3. Generate bill serial number
4. Create sellbill record
5. Loop through products:
- Validate stock availability
- Create sellbilldetail records
- Decrease stock via decreaseProductQuantity()
- Track serial numbers if applicable
- Calculate profit via quickProfitRow()
6. Handle multi-payment (cash, visa, bank)
7. Update customer debt via updateClientDebt()
8. Update cash register via getSaveValueAndPlus()
9. Generate accounting entries via doBillDailyEntry()
10. Handle collective products (recipes) if applicable
11. Commit transaction
12. Redirect to success page
Key Variables:
- โข
$sellbilldate- Bill date - โข
$sellbilltotalbill- Total before discount - โข
$sellbillaftertotalbill- Total after discount - โข
$sellbilldiscountval- Discount amount - โข
$sellbilltaxval- Tax amount - โข
$sellbilltotalPayed- Amount paid - โข
$sellbillclientId- Customer ID - โข
$sellbillstoreId- Warehouse ID - โข
$sellbillsaveid- Cash register ID
Dependencies:
- โข
decreaseProductQuantity()- Stock decrease - โข
updateClientDebt()- Customer balance - โข
doBillDailyEntry()- Accounting entries - โข
quickProfitRow()- Profit calculation
---
2. decreaseProductQuantity() - Stock Decrease Logic
Location: Line 10161
Purpose: Decrease product quantity in stock when selling
Function Signature:
function decreaseProductQuantity(
$storedetailId, // Stock record ID
$productquantityBefore, // Quantity before sale
$productChangeAmount, // Quantity to decrease
$colName, // Detail table column name
$detailId, // Bill detail ID
$productId, // Product ID
$tableName, // Bill table name
$prototal, // Line total
$billDiscountVal, // Bill discount
$billTotalBeforeDiscount, // Bill total before discount
$isreturn, // Is return operation
$isadd, // Is add operation
$sizeColorStoreDetailId, // Size/color variant ID
$sizeId, // Size ID
$colorId // Color ID
)
Process Flow:
1. Calculate new quantity: $productquantityAfter = $productquantityBefore - $productChangeAmount
2. Update storedetail record with new quantity
3. Insert storereport movement record
4. Handle FIFO/LIFO cost tracking via getBuyPriceFromAndHandleBuyPricesHistoryBook()
5. Calculate profit via lastAndMeanBuyPrice_Sell()
6. Handle size/color variants if applicable
Critical Bug Fixed (Dec 19, 2024):
- โข Changed condition from
(!empty($allStoredetailData) && is_array($allStoredetailData) ? count($allStoredetailData) : 0) > 0 - โข To:
!empty($allStoredetailData) - โข Reason:
getStoreDetails()returns object, not array.is_array()returned false in PHP 8.2, causing stock to never decrease.
---
3. doBillDailyEntry() - Generate Accounting Entries
Location: Line 13612
Purpose: Auto-generate journal entries for sales bills
Function Signature:
function doBillDailyEntry(
$billtype, // Bill type (sell/return/combined)
$sellbillId, // Sell bill ID
$returnsellbillId, // Return bill ID
$sellbilltotalPayed, // Amount paid
$saveid, // Cash register ID
$visa, // Visa amount
$visaAccount, // Visa account ID
$gen4totalinput, // Additional inputs
$clientTreeId, // Customer account ID
$taxOfDiscountVal, // Tax on discount
$billDiscountVal, // Discount amount
$sellbilltotalbill, // Total before discount
$sellbillAftertotalBill, // Total after discount
$taxVal, // Tax amount
$sellCostsArray, // Additional costs
$sellbillstoreId, // Store ID
$firstProductId, // First product ID
$billoperationid // Operation ID
)
Accounting Logic:
For Regular Sale (Cash):
Debit: Cash Register (sellbilltotalPayed)
Credit: Sales Revenue (sellbillAftertotalBill - taxVal)
Credit: Tax Payable (taxVal)
Debit: Customer Receivable (sellbillAftertotalBill - sellbilltotalPayed)
For Sale with Visa:
Debit: Cash Register (cash amount)
Debit: Bank Account (visa amount)
Credit: Sales Revenue (total)
Process Flow:
1. Get account tree IDs via getsellTreeIdAccontId()
2. Create dailyentry master record
3. Create debit entries (dailyentrydebtor):
- Cash register (if cash payment)
- Bank account (if visa payment)
- Customer receivable (if credit)
4. Create credit entries (dailyentrycreditor):
- Sales revenue account
- Tax payable account
5. Handle additional costs/expenses
6. Link entry to bill via dailyentrymodelid
---
4. quickProfitRow() - Calculate Profit per Line
Location: Line 10551
Purpose: Calculate gross profit for each product sold
Function Signature:
function quickProfitRow(
$myproduct, // Product object
$finalQuantity, // Quantity sold
$isreturn, // Is return
$isadd, // Is add
$prototal, // Line total
$billDiscountVal, // Bill discount
$billTotalBeforeDiscount, // Bill total
$storeId // Store ID
)
Calculation Logic:
// Get cost from FIFO/LIFO tracking
$costData = getBuyPriceFromAndHandleBuyPricesHistoryBook(
$storeId, $productId, $sizeId, $colorId, $productBuyPrice, $finalQuantity, 1
);
// Calculate profit
$profit = ($sellPrice * $quantity) - $costData['totalCost'];
// Store in faida (profit) table
INSERT INTO faida (
productid, storeid, quantity, sellprice, buyprice, profit, billid, billtype
)
---
5. editsellBill() - Edit Existing Sales Bill
Location: Line 2041
Purpose: Modify an existing sales bill
Process Flow:
1. Begin transaction
2. Load existing bill data
3. Reverse previous operations:
- Restore stock quantities
- Reverse customer debt changes
- Reverse cash register changes
- Delete old accounting entries
- Delete old profit records
4. Delete old sellbilldetail records
5. Apply new operations (same as add()):
- Decrease stock with new quantities
- Update customer debt
- Update cash register
- Generate new accounting entries
6. Update sellbill master record
7. Commit transaction
Critical Notes:
- โข Editing is essentially delete + re-add
- โข All linked records are cleaned and recreated
- โข Serial numbers are re-assigned
---
6. delete() - Delete Sales Bill
Location: Line 9530
Purpose: Soft delete or hard delete a sales bill
Function Signature:
function delete($sellbillid, $savePaymentDateWithBillDate = 0)
Process Flow:
1. Begin transaction
2. Load bill data
3. Reverse all operations:
- Restore stock: call increaseProductQuantity()
- Reverse customer debt: subtract bill total
- Reverse cash register: subtract payment
- Reverse bank movements
- Delete accounting entries
- Delete profit records
- Restore serial numbers (mark as unsold)
4. Soft delete: Set sellbill.conditions = 1
5. Commit transaction
Soft Delete:
- โข Records remain in database with
conditions = 1 - โข Excluded from reports via
WHERE conditions = 0 - โข Preserves audit trail
---
7. showDetail() - View Bill Details
Location: Line 4610
Purpose: Display full details of a sales bill for viewing/printing
Process Flow:
1. Load sellbill master record
2. Load customer data
3. Load bill details (sellbilldetail records)
4. Load product names, prices
5. Calculate totals, discounts, tax
6. Check for serial numbers
7. Prepare Smarty template data
8. Display via selldetail.html template
Template Variables:
- โข
$sellbillData- Bill master data - โข
$sellbillDetailArr- Array of line items - โข
$clientData- Customer information - โข
$totalBeforeDiscount- Subtotal - โข
$totalAfterDiscount- Final total - โข
$taxAmount- Tax amount
---
8. showAll() - List All Sales Bills
Location: Line 6933
Purpose: Display paginated list of all sales bills
Features:
- โข Pagination (50 bills per page)
- โข Search by: serial, client name, date range
- โข Sort by: date, client, total
- โข Filter by: bill type, paid/unpaid, date
- โข Export to Excel/PDF
SQL Query:
SELECT
sellbill.*,
client.clientname,
user.username,
billname.billname
FROM sellbill
LEFT JOIN client ON sellbill.sellbillclientid = client.clientid
LEFT JOIN user ON sellbill.sellbilluserid = user.userid
LEFT JOIN billname ON sellbill.sellbillbillnameid = billname.billnameid
WHERE sellbill.conditions = 0
ORDER BY sellbill.sellbilldate DESC
LIMIT 50 OFFSET 0
---
9. getBuyPriceFromAndHandleBuyPricesHistoryBook() - FIFO/LIFO Costing
Location: Line 13073
Purpose: Track product costs using FIFO/LIFO method
Function Signature:
function getBuyPriceFromAndHandleBuyPricesHistoryBook(
$storeId, // Store ID
$productid, // Product ID
$sizeid, // Size ID
$colorid, // Color ID
$productBuyPrice, // Fallback buy price
$soldQuantity, // Quantity sold
$type = 0 // 0=get cost, 1=decrease stock
)
FIFO Logic:
1. Query buypricesh istorybook for oldest stock:
SELECT * FROM buypricesh istorybook
WHERE storeid = ? AND productid = ?
AND quantity > 0
ORDER BY buybilldate ASC, buypricesh istorybookid ASC
2. Allocate sold quantity from oldest batches first
3. Decrease batch quantities
4. Return weighted average cost
Example:
Stock batches:
- Batch 1: 10 units @ $5 (oldest)
- Batch 2: 20 units @ $6
- Batch 3: 15 units @ $7
Sell 25 units:
- Take 10 from Batch 1 โ Cost: $50
- Take 15 from Batch 2 โ Cost: $90
- Total cost: $140
- Average cost per unit: $5.60
---
10. addRestaurantBill() - Restaurant/POS Orders
Location: Line 10807
Purpose: Create sales bill from restaurant table order
Function Signature:
function addRestaurantBill($restaurantOrder, $restaurantOrderDetails)
Process Flow:
1. Load restaurant order data
2. Convert order details to sell bill format
3. Call add() with restaurant-specific parameters
4. Mark restaurant order as completed
5. Print kitchen receipt
6. Print customer receipt
Integration:
- โข Restaurant module creates temporary orders
- โข Kitchen prints order tickets
- โข Cashier converts order to sell bill
- โข Bill payment completes the flow
---
11. addOnlineOrderAsSellBill() - Online Store Integration
Location: Line 11247
Purpose: Convert online store order to ERP sales bill
Function Signature:
function addOnlineOrderAsSellBill(
$onlineOrderId, // Online order ID
$onlineStoreId, // Online store ID
$clientChoosen, // Customer selection
$onlineStoreSetting // Store settings
)
Process Flow:
1. Fetch order data from online store API
2. Match/create customer in ERP
3. Map online products to ERP products
4. Create sell bill
5. Update online order status
6. Send confirmation email
Supported Platforms:
- โข Custom online store
- โข WooCommerce integration
- โข Shopify integration (via API)
---
12. getStoreDetails() - Get Stock Record
Location: Line 10709
Purpose: Retrieve stock detail record for product
Function Signature:
function getStoreDetails(
$storeId, // Store ID
$productId, // Product ID
$unittype, // Unit type
$sizeColorStoreDetailId, // Size/color variant ID
$sizeId, // Size ID
$colorId // Color ID
)
Returns: storedetail object or null
Logic:
- โข If size/color variant: query sizecolorstoredetail
- โข Else: query storedetail
- โข Returns first matching record
Used By:
- โข
decreaseProductQuantity() - โข
increaseProductQuantity() - โข Stock availability checks
---
๐ Workflows
Workflow 1: Create Sales Bill (Cash Sale)
---
Workflow 2: Sales Return Process
---
๐ URL Routes & Actions
| URL Parameter | Function Called | Description | |
|---|---|---|---|
| `do=add` | `add()` | Display new bill form | |
| `do=addsellBill` | `add()` | Process new sales bill | |
| `do=editsellBill` | `editsellBill()` | Edit existing sales bill | |
| `do=editreturnsellBill` | `editreturnsellBill()` | Edit return bill | |
| `do=editsellandrutrnBill` | `editsellandrutrnBill()` | Edit combined bill | |
| `do=delete` | `delete()` | Delete/cancel bill | |
| `do=showDetail` | `showDetail()` | View bill details | |
| `do=show` or `do=all` | `showAll()` | List all bills | |
| `do=showoffers` | `showAllOffers()` | List sales quotations | |
| `do=saveOfferAsBill` | `saveOfferAsBill()` | Convert quotation to bill | |
| `do=addresBill` | Restaurant bill | Add restaurant order | |
| `do=endKitchenBill` | Kitchen flow | Complete kitchen order | |
| `do=onlineOrderConfirm` | `addOnlineOrderAsSellBill()` | Process online order | |
| `do=billToExcel` | Excel export | Export bill to Excel | |
| `do=billToPDF` | PDF export | Export bill to PDF | |
| `do=serializeProducts` | Serial tracking | Assign serial numbers | |
| `do=showallajax` | `showallajax()` | Ajax bill list | |
| `do=addObgyVisit` | `addObgyVisit()` | OBGY visit billing | |
| `do=addProductToOrder` | `addProductToOrder()` | Add product to quotation | |
| `do=removeProductFromOrder` | `removeProductFromOrder()` | Remove from quotation |
๐ Known Issues & Fixes
1. Stock Quantity Goes to Zero Bug (CRITICAL - FIXED)
Location: Line 10161 (decreaseProductQuantity)
Date Fixed: December 19, 2024
Problem:
When selling products, stock quantity became 0 instead of decreasing by sold amount.
Root Cause:
if ((!empty($allStoredetailData) && is_array($allStoredetailData) ? count($allStoredetailData) : 0) > 0) {
decreaseProductQuantity(...);
}
- โข
getStoreDetails()returns an object, not an array - โข In PHP 8.2,
is_array($object)returnsfalse - โข Ternary evaluates to
0 > 0= false - โข Stock decrease function never called
Fix Applied:
if (!empty($allStoredetailData)) {
decreaseProductQuantity(...);
}
Files Modified:
- โข sellbillController.php (20+ occurrences fixed via sed)
---
2. Attempt to Assign Property on Null
Location: Multiple functions
Error: Fatal error: Uncaught Error: Attempt to assign property on null
Fix Pattern:
$dailyEntry = new stdClass();
$dailyEntry->entryComment = $billComment;
Affected Functions:
- โข
quickProfitRow()- Line 10551 - โข
quickProfitBill()- Line 10637 - โข
doBillDailyEntry()- Line 13612
---
3. implode() TypeError
Error: TypeError: implode(): Argument must be array
Fix:
$tagids = isset($_POST['tagids']) && is_array($_POST['tagids']) ? $_POST['tagids'] : [];
$mySellbill->tagids = is_array($tagids) ? implode(',', $tagids) : '';
---
4. Collective Product Cost Calculation
Location: Lines 12691-12887
Issue: Collective products (recipes/bundles) need special handling
Logic:
- โข Collective product = bundle of ingredients
- โข When sold: decrease ingredient stock, not bundle stock
- โข Cost = sum of ingredient costs
- โข Profit = sell price - ingredient costs
Functions:
- โข
ifCollectiveProductReCalculateCost()- Recalculate cost - โข
ifCollectiveDecreaseItsIngriedientsInStore()- Decrease ingredients - โข
ifCollectiveIncreaseItsIngriedientsInStore()- Restore ingredients (on return)
---
5. FIFO Cost Tracking with Returns
Location: Line 13121 (decreaseSellQuantityHandleBuyPricesHistoryBook)
Problem: Returns must restore original FIFO batches
Solution:
- โข Store batch allocation IDs in sell bill
- โข On return: restore exact batches that were decreased
- โข Prevents cost distortion
---
๐ Security & Permissions
Authentication
- โข All actions require active session (
$_SESSION['userid']) - โข Session regenerated on every request
- โข CSRF protection via session tokens
Authorization
- โข User group permissions checked via
usergrouptable - โข Properties table defines access rights
- โข Bill deletion requires admin permission
Input Validation
- โข All POST/GET parameters filtered
- โข Numeric fields cast to int/float
- โข SQL injection prevented by RedBeanPHP ORM
- โข XSS prevented by Smarty template escaping
Audit Trail
- โข All operations logged with userid, date, MAC address
- โข Soft deletes preserve history
- โข Daily entry records provide full audit trail
---
๐งช Testing & Debugging
Enable Debug Mode
// In sellbillController.php
error_reporting(E_ALL);
ini_set('display_errors', 1);
Debug Stock Issues
-- Check current stock
SELECT * FROM storedetail WHERE productid = [ID];
-- Check stock movements
SELECT * FROM storereport
WHERE storereportproductid = [ID]
ORDER BY storereportdate DESC
LIMIT 20;
-- Check FIFO batches
SELECT * FROM buypricesh istorybook
WHERE productid = [ID] AND quantity > 0;
Debug Customer Debt
-- Check customer balance
SELECT clientdebt FROM client WHERE clientid = [ID];
-- Check debt history
SELECT * FROM clientdebtchange
WHERE clientid = [ID]
ORDER BY clientdebtchangedate DESC;
Debug Accounting Entries
-- Check journal entries for bill
SELECT * FROM dailyentry
WHERE dailyentrymodelid = [BILL_ID]
AND tablename = 'sellbill';
-- Check debit/credit balance
SELECT
(SELECT SUM(value) FROM dailyentrydebtor WHERE dailyentryid = de.dailyentryid) as debits,
(SELECT SUM(value) FROM dailyentrycreditor WHERE dailyentryid = de.dailyentryid) as credits
FROM dailyentry de
WHERE dailyentrymodelid = [BILL_ID];
Common Debug Points
// After stock decrease
file_put_contents('/tmp/stock_debug.log',
"Product: $productId, Before: $qtyBefore, After: $qtyAfter\n", FILE_APPEND);
// After FIFO allocation
var_dump($costData);
exit;
// Check transaction state
if (R::testConnection()) {
echo "DB connected\n";
}
---
๐ Performance Considerations
Optimization Tips
1. OPcache: Critical for loading 500+ DAO files
2. Indexes: Ensure indexes on:
- sellbill.sellbillclientid
- sellbill.sellbilldate
- sellbilldetail.sellbillid
- storedetail.productid, storeid
3. Batch Operations: Use transactions for multi-line bills
4. Avoid N+1 Queries: Load products in single query
5. Session Caching: Cache frequently used data (bill settings, etc.)
Known Slow Queries
-- This query is slow on large datasets (add index)
SELECT * FROM sellbill
WHERE sellbilldate BETWEEN ? AND ?
AND conditions = 0
ORDER BY sellbilldate DESC;
-- Fix: Add composite index
CREATE INDEX idx_date_conditions ON sellbill(sellbilldate, conditions);
---
๐ Related Documentation
- โข CLAUDE.md - PHP 8.2 migration guide
- โข buyBillController.md - Purchase operations
- โข dailyentryfun.php - Accounting entry functions
- โข affectplugins.php - Stock movement plugins
---
Documented By: AI Assistant
Review Status: โ Complete
Next Review: When major changes occur