# 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
- [x] Create new sales bills
- [x] Edit existing sales bills
- [x] Delete sales bills
- [x] View sales bill details
- [x] Print sales bills
- [x] Manage sales returns
- [x] Track serial numbers
- [x] Generate accounting entries
- [x] Update inventory quantities
- [x] Process customer payments
- [x] Handle multi-payment methods (cash, visa, credit)
- [x] Restaurant/kitchen orders
- [x] Online order processing
- [x] Excel/PDF export

### Related Controllers
- [buyBillController.php](buyBillController.md) - Purchase operations
- [clientController.php](clientController.md) - Customer management
- [productController.php](productController.md) - Product management
- [storeController.php](storeController.md) - 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 |

### Inventory Tables (Stock Management)
| 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 |

### Financial Tables
| 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 |

### Reference Tables
| 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**:
```php
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**:
```php
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**:
```php
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**:
```php
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**:
```php
// 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**:
```php
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**:
```sql
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**:
```php
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:
```sql
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**:
```php
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**:
```php
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**:
```php
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)
```
┌─────────────────────────────────────────────────────────────┐
│                    START: User clicks "Add Bill"             │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Display Bill Form (do=add)                               │
│     - Select customer                                        │
│     - Select products                                        │
│     - Enter quantities, prices                               │
│     - Apply discount, tax                                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Submit Form (do=addsellBill)                             │
│     - Validate inputs                                        │
│     - Check stock availability                               │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Begin Database Transaction                               │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Create sellbill Master Record                            │
│     - Generate serial number                                 │
│     - Store date, customer, totals                           │
│     - INSERT INTO sellbill                                   │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. Loop Through Products                                    │
│     FOR EACH product line item:                              │
│       │                                                       │
│       ├─→ Create sellbilldetail record                       │
│       │                                                       │
│       ├─→ Call decreaseProductQuantity()                     │
│       │   ├─ Update storedetail.productquantity              │
│       │   ├─ Insert storereport record                       │
│       │   └─ Handle FIFO cost tracking                       │
│       │                                                       │
│       ├─→ Call quickProfitRow()                              │
│       │   └─ Insert profit record                            │
│       │                                                       │
│       └─→ Handle serial numbers (if applicable)              │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  6. Update Customer Debt                                     │
│     - Calculate debt increase                                │
│     - Call updateClientDebt()                                │
│     - Update client.clientdebt                               │
│     - Insert clientdebtchange record                         │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  7. Update Cash Register                                     │
│     - Call getSaveValueAndPlus()                             │
│     - Update save.savevalue                                  │
│     - Insert savedaily record                                │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  8. Generate Accounting Entries                              │
│     - Call doBillDailyEntry()                                │
│     - Insert dailyentry                                      │
│     - Insert dailyentrydebtor (Cash Register)                │
│     - Insert dailyentrycreditor (Sales Revenue)              │
│     - Insert dailyentrycreditor (Tax Payable)                │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  9. Commit Transaction                                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  10. Redirect to Success / Print Bill                        │
│      - Show success message                                  │
│      - Option to print bill                                  │
│      - Redirect to new bill form                             │
└─────────────────────────────────────────────────────────────┘
```

---

### Workflow 2: Sales Return Process
```
┌─────────────────────────────────────────────────────────────┐
│              START: Customer Returns Product                 │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  1. Find Original Bill (do=showDetail)                       │
│     - Search by serial number or customer                    │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  2. Create Return Bill (do=add with return flag)             │
│     - Select products to return                              │
│     - Enter return quantities                                │
│     - Apply return discount if any                           │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Begin Transaction                                        │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Create returnsellbill Record                             │
│     - Link to original sellbill                              │
│     - Store return date, amounts                             │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  5. Process Returned Products                                │
│     FOR EACH returned item:                                  │
│       │                                                       │
│       ├─→ Create returnsellbilldetail                        │
│       │                                                       │
│       ├─→ Call increaseProductQuantity()                     │
│       │   ├─ Restore stock quantity                          │
│       │   ├─ Insert storereport (type=return)                │
│       │   └─ Update FIFO tracking                            │
│       │                                                       │
│       └─→ Reverse profit calculation                         │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  6. Update Customer Debt (Decrease)                          │
│     - Subtract return amount from debt                       │
│     - Insert clientdebtchange (negative)                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  7. Refund Payment                                           │
│     - Decrease cash register balance                         │
│     - Insert savedaily (negative amount)                     │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  8. Generate Reverse Accounting Entries                      │
│     - Debit: Sales Returns account                           │
│     - Credit: Cash Register                                  │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  9. Commit Transaction                                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  10. Print Return Receipt                                    │
└─────────────────────────────────────────────────────────────┘
```

---

## 🌐 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**:
```php
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)` returns `false`
- Ternary evaluates to `0 > 0` = false
- Stock decrease function never called

**Fix Applied**:
```php
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**:
```php
$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**:
```php
$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 `usergroup` table
- 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
```php
// In sellbillController.php
error_reporting(E_ALL);
ini_set('display_errors', 1);
```

### Debug Stock Issues
```sql
-- 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
```sql
-- 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
```sql
-- 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
```php
// 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
```sql
-- 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](/Applications/AMPPS/www/erp19/CLAUDE.md) - PHP 8.2 migration guide
- [buyBillController.md](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
