SalesNumbersReport Documentation

Sales Numbers Report Controller Documentation

File: /controllers/salesNumbersReport.php

Purpose: Generate comprehensive sales numbers and movement reports by product categories with store inventory tracking

Last Updated: December 21, 2024

Total Functions: 16

Lines of Code: ~1107

---

๐Ÿ“‹ Overview

The Sales Numbers Report Controller provides detailed product movement and sales numbers analysis focused on inventory tracking and profit calculations. It offers:

Primary Functions

Related Controllers

---

๐Ÿ—„๏ธ Database Tables

Inventory Movement Tables

Table NamePurposeKey Columns
**storereport**Product movement logstorereportid, productid, storeid, storereportdate, productquantity, storereporttype, tablename, storereportmodelid
**store**Store locationsstoreid, storename
**storedetail**Current inventory levelsstoredetailid, productid, storeid, productquantity
### Transaction Tables

Table NamePurposeKey Columns
**sellbill**Sales billssellbillid, sellbillclientid, sellbilldate, sellbilltotalbill, conditions
**returnsellbill**Sales returnsreturnsellbillid, returnsellbillclientid, returnsellbilldate, returnsellbilltotalbill
**buybill**Purchase billsbuybillid, buybillsupplierid, buybilldate, buybillaftertotalbill, conditions
**returnbuybill**Purchase returnsreturnbuybillid, returnbuybillsupplierid, returnbuybilldate, returnbuybillaftertotalbill
### Product Tables

Table NamePurposeKey Columns
**product**Product masterproductid, productname, productcatid, productbuyprice
**productcat**Product categoriesproductcatid, productcatname, productcatparent
**productunit**Unit conversionsproductunitid, productid, unitid, productnumber
### Supporting Tables

Table NamePurposeKey Columns
**bills**Service billsid, clientid, billdate, finalnetbillvalue
**programsettings**System settingsprogramsettingsid
**youtubelink**Tutorial videosyoutubelinkid, title, url
---

๐Ÿ”‘ Key Functions

1. Default Action - Product Movement Report

Location: Line 188 ($do == "show" or empty($do))

Purpose: Generate product movement analysis by category

Function Signature:

// REQUEST parameters:
$startDate = $_REQUEST['from'];      // Start date filter
$endDate = $_REQUEST['to'];          // End date filter  
$productCatId = $_REQUEST['productCatId']; // Product category filter
$storeId = $_REQUEST['storeId'];     // Store filter

Process Flow:

1. Load authentication and program settings

2. Handle multi-store access control:

   if ($_SESSION['searchinonestore'] == 0) {
       if ($_SESSION['storeids'] == 0) {
           $stores = $StoreEX->queryByConditions(); // All stores
       } else {
           $stores = $StoreEX->queryByConditions(' and store.storeId in (' . $_SESSION['storeids'] . ')');
       }
   }
   ```
3. Build query filters for date range and store selection
4. Load product categories for dropdown
5. If category selected, load products and analyze movements:
   ```php
   if (!empty($productCatId)) {
       $productData = $myProductEx->queryByProductCatIdEX2($productCatId, $storeQuery);
       $productsArr = array();
       foreach ($productData as $val) {
           $productId = $val->productId;
           $arrContainer = showByStoreAndProductAndDate($productId, $query, $val->productquantity);
           if (!empty($arrContainer)) {
               array_push($productsArr, $arrContainer);
           }
       }
   }
   ```
6. Display results via `profitproductview/sallesProductCat.html` template

---

### 2. **showByStoreAndProductAndDate()** - Core Movement Analysis
**Location**: Line 291  
**Purpose**: Analyze individual product movements and validate against actual transactions

**Function Signature**:
php

function showByStoreAndProductAndDate($productid, $query, $productquantity)

**Process Flow**:
1. Load movement data from storereport table:
   ```php
   $storemovementData = $StorereportEX->queryWithProductAndStoreAndDateandorderMo($productid, $query);
   ```

2. Process each movement and validate against source transactions:
   ```php
   foreach ($storemovementData as $val) {
       // Validate movement against source document
       if ($val->tablename == "sellbillController.php") {
           $res = R::count("sellbill", "sellbillid =" . $val->storereportmodelid . " and conditions = 0");
       } elseif ($val->tablename == "buyBillController.php") {
           $res = R::count("buybill", "buybillid =" . $val->storereportmodelid . " and conditions = 0");
       }
       
       if ($res <= 0) {
           $val->productquantity = 0; // Nullify invalid movements
       }
   }
   ```

3. Calculate plus/minus quantities by movement type:
   ```php
   if ($storereporttype == 1) {
       $minQuantity += $resProductQuantity; // Outbound movement
   } else {
       $plusQuantity += $resProductQuantity; // Inbound movement
   }
   ```

4. Return movement summary:
   ```php
   $arrContainer = array(
       'productName' => $resProductName,
       'plusQuantity' => $plusQuantity,     // Total inbound
       'minQuantity' => $minQuantity,       // Total outbound
       'diff' => $plusQuantity - $minQuantity, // Net movement
       'productquantity' => $productquantity    // Current inventory
   );
   ```

---

### 3. **getProductInBillByDateAndProductId()** - Transaction Analysis
**Location**: Line 358  
**Purpose**: Detailed analysis of all transaction types for products in a category

**Function Signature**:
php

function getProductInBillByDateAndProductId($startDate, $endDate, $productCatId)

**Process Flow**:
1. Load all stores and products in category
2. For each store and product combination:
   - Load last inventory position from storereport
   - Calculate sales totals from multiple sources
   - Calculate purchase totals from multiple sources
   - Calculate returns and adjustments
   - Generate final inventory reconciliation

**Transaction Sources Analyzed**:
- Regular sales (`getTotalSellPriceByDateAndProductId()`)
- Additional sales (`getTotalAditionalSellPriceByDateAndProductId()`)
- Service bills (`getTotalbills()`)
- Sales returns (`getTotalReturnSellPriceByDateAndProductId()`)
- Purchase bills (`getTotalBuyPriceByDateAndProductId()`)
- Service returns (`getTotalbillsReturn()`)

**Final Calculation**:
php

$final = ($buydif + $productafter) - $selldif;

// Where:

// $buydif = Total purchases - purchase returns

// $productafter = Starting inventory

// $selldif = Total sales - sales returns

---

### 4. **Service Bills Analysis Functions**
**Location**: Lines 462-533  
**Purpose**: Analyze service bill transactions

#### **getTotalbills()** - Service Bill Totals
php

function getTotalbills($startDate, $endDate, $ProductIdselected, $storid)

**Process**: 
1. Query service bills with storereport linkage
2. Convert quantities using product unit factors
3. Return total quantities moved

#### **getTotalbillsReturn()** - Service Bill Returns
php

function getTotalbillsReturn($startDate, $endDate, $ProductIdselected, $storid)

**Process**: Similar to bills but for returns, calculating returned quantities

---

### 5. **Sales Transaction Analysis Functions**
**Location**: Lines 536-686  
**Purpose**: Analyze regular sales transactions with detailed pricing

#### **getTotalSellPriceByDateAndProductId()** - Regular Sales Analysis
**Location**: Line 536  
**Purpose**: Calculate sales totals with discount and profit analysis

**Key Calculations**:
php

foreach ($sellbilldetailData as $sellbilldetail) {

$quantity = $sellbilldetail->sellbilldetailquantity;

$price = $sellbilldetail->sellbilldetailprice;

$totalPrice = $sellbilldetail->sellbilldetailtotalprice;

$discountValue = $sellbilldetail->discountvalue;

$buyprice = $sellbilldetail->buyprice;

// Calculate percentage discount

if ($sellbillDiscountType == 2) {

$sellbillDiscount = ($sellbillDiscount / 100) * $sellbillTotalBill;

}

// Unit conversion

$productnumber = $myProductunitEx->getProductNumber($productunitId);

$quantityUnit += ($quantity * $productnumber);

// Profit calculation

$allbuyprice = $quantity * $buyprice;

$finalallbuyprice += $allbuyprice;

// Net selling price after discounts

$sellPriceForOneProduct = $totalPriceBeforeDiscount -

(($discountValue * $quantity) + $sellbillDiscount);

$sellPrice += $sellPriceForOneProduct;

}

return array($sellPrice, $quantityUnit, $finalallbuyprice);

---

### 6. **Purchase Transaction Analysis Functions**
**Location**: Lines 811-893  
**Purpose**: Analyze purchase transactions and costs

#### **getTotalBuyPriceByDateAndProductId()** - Purchase Analysis
**Location**: Line 811  
**Purpose**: Calculate purchase quantities from multiple bill types

**Process Flow**:
1. Query regular purchase bills (`buybilldetailData`)
2. Query combined purchase bills (`buyandruturn`)  
3. Query purchase returns (`ruturndata`)
4. Query purchase returns from combined bills (`ruturndataformbyandreturn`)
5. Convert all quantities to base units using product unit factors
6. Return net purchase quantities (purchases - returns)

**Return Structure**:
php

return array($quantityUnit, $ruturnquntity);

// $quantityUnit = Total purchased quantities

// $ruturnquntity = Total returned quantities

---

### 7. **Product Hierarchy Functions**
**Location**: Lines 1024-1107  
**Purpose**: Product categorization and hierarchy management

#### **getProductCats()** - Product Category Hierarchy
**Location**: Line 1068  
**Purpose**: Build complete product category hierarchy for dropdown navigation

**Process Flow**:
1. Load all products with categories
2. For each product, build recursive category path
3. Assign hierarchical paths to template variables
4. Enable multi-level category navigation

#### **fetch_recursive()** - Category Path Builder
**Location**: Line 1090  
**Purpose**: Recursively build category paths

**Recursive Logic**:
php

function fetch_recursive($parentid, $categories) {

$catData = $myProductcatEx->getCategoryAndParentByCatId($parentid);

if (count($catData) > 0) {

$categories .= $catData->productCatName . '/';

$newParentId = $catData->productCatParent;

if ($newParentId != 0) {

$newParentName = $catData->parentName;

$categories .= $newParentName . '/';

fetch_recursive($newParentId, $categories);

}

}

return substr($categories, 0, strlen($categories) - 1); // Remove trailing slash

}

---

## ๐Ÿ”„ Workflows

### Workflow 1: Product Movement Analysis

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ START: Select Category and Date Range โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 1. Load System Configuration โ”‚

โ”‚ - Check multi-store access permissions โ”‚

โ”‚ - Load available stores for user โ”‚

โ”‚ - Apply authentication โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 2. Build Query Filters โ”‚

โ”‚ - Date range filter for storereport.storereportdate โ”‚

โ”‚ - Store filter based on user permissions โ”‚

โ”‚ - Build storeQuery for inventory lookup โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 3. Load Products by Category โ”‚

โ”‚ IF productCatId provided: โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ”œโ”€โ†’ Load all products in category โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ”œโ”€โ†’ Apply store filter to product query โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ””โ”€โ†’ Get current inventory levels โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 4. Analyze Each Product's Movements โ”‚

โ”‚ FOR EACH product in category: โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ”œโ”€โ†’ Call showByStoreAndProductAndDate() โ”‚

โ”‚ โ”‚ โ”‚ โ”‚

โ”‚ โ”‚ โ”œโ”€โ†’ Load movement data from storereport โ”‚

โ”‚ โ”‚ โ”‚ โ”‚

โ”‚ โ”‚ โ”œโ”€โ†’ Validate movements against source documents โ”‚

โ”‚ โ”‚ โ”‚ โ”œโ”€ Check sellbill.conditions = 0 โ”‚

โ”‚ โ”‚ โ”‚ โ””โ”€ Check buybill.conditions = 0 โ”‚

โ”‚ โ”‚ โ”‚ โ”‚

โ”‚ โ”‚ โ”œโ”€โ†’ Calculate plus/minus quantities โ”‚

โ”‚ โ”‚ โ”‚ โ”œโ”€ storereporttype = 0: Inbound (purchases) โ”‚

โ”‚ โ”‚ โ”‚ โ””โ”€ storereporttype = 1: Outbound (sales) โ”‚

โ”‚ โ”‚ โ”‚ โ”‚

โ”‚ โ”‚ โ””โ”€โ†’ Generate movement summary โ”‚

โ”‚ โ”‚ โ”œโ”€ Total inbound movements โ”‚

โ”‚ โ”‚ โ”œโ”€ Total outbound movements โ”‚

โ”‚ โ”‚ โ”œโ”€ Net movement (in - out) โ”‚

โ”‚ โ”‚ โ””โ”€ Current inventory level โ”‚

โ”‚ โ”‚ โ”‚

โ”‚ โ””โ”€โ†’ Add to products array if movements found โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”‚

โ–ผ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

โ”‚ 5. Generate Report Output โ”‚

โ”‚ - Display category name and date range โ”‚

โ”‚ - Show movement summary for each product โ”‚

โ”‚ - Present plus/minus/net movement analysis โ”‚

โ”‚ - Include current inventory levels โ”‚

โ”‚ - Load tutorial videos โ”‚

โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

---

## ๐ŸŒ URL Routes & Actions

| URL Parameter | Function Called | Description |
|---------------|----------------|-------------|
| No `do` parameter or `do=show` | Default action | Product movement analysis by category |

### Required Parameters

**Product Movement Report**:
- `productCatId` - Product category ID (required for analysis)
- `from` - Start date (YYYY-MM-DD, optional)
- `to` - End date (YYYY-MM-DD, optional)
- `storeId` - Store ID filter (optional, respects user permissions)

**Example URLs**:

salesNumbersReport.php?productCatId=5&from=2024-01-01&to=2024-01-31&storeId=1

salesNumbersReport.php?productCatId=10&from=2024-01-01&to=2024-01-31

---

## ๐Ÿงฎ Calculation Methods

### Movement Type Classification
php

// storereporttype values:

// 0 = Inbound movement (purchases, returns to inventory)

// 1 = Outbound movement (sales, issues from inventory)

if ($storereporttype == 1) {

$minQuantity += $resProductQuantity; // Outbound

} else {

$plusQuantity += $resProductQuantity; // Inbound

}

$netMovement = $plusQuantity - $minQuantity;

### Movement Validation Against Source Documents
php

// Validate that the movement corresponds to a valid transaction

if ($val->tablename == "sellbillController.php") {

$res = R::count("sellbill", "sellbillid = " . $val->storereportmodelid . " and conditions = 0");

} elseif ($val->tablename == "buyBillController.php") {

$res = R::count("buybill", "buybillid = " . $val->storereportmodelid . " and conditions = 0");

}

// If source document deleted/invalid, nullify movement

if ($res <= 0) {

$val->productquantity = 0;

}

### Profit Analysis with Unit Conversion
php

// Unit conversion from transaction unit to base unit

$productnumber = $myProductunitEx->getProductNumber($productunitId);

$quantityUnit += ($quantity * $productnumber);

// Cost calculation

$allbuyprice = $quantity * $buyprice;

$finalallbuyprice += $allbuyprice;

// Revenue calculation with discounts

$totalPriceBeforeDiscount = $price * $quantity;

$sellPriceForOneProduct = $totalPriceBeforeDiscount -

(($discountValue * $quantity) + $sellbillDiscount);

// Profit = Revenue - Cost

$profit = $sellPriceForOneProduct - $allbuyprice;

---

## ๐Ÿ”’ Security & Permissions

### Multi-Store Access Control
php

// Check if user has access to multiple stores

if ($_SESSION['searchinonestore'] == 0) {

if ($_SESSION['storeids'] == 0) {

// User can access all stores

$stores = $StoreEX->queryByConditions();

} else {

// User limited to specific stores

$stores = $StoreEX->queryByConditions(' and store.storeId in (' . $_SESSION['storeids'] . ')');

}

} else {

// User limited to single store

$query .= ' and storereport.storeid = ' . $_SESSION['storeid'] . ' ';

}

### Authentication
php

include_once("../public/authentication.php");

### Data Integrity Validation
- Movements validated against source documents
- Invalid/deleted transactions nullified in reports
- Store access controlled by user permissions

---

## ๐Ÿ“Š Performance Considerations

### Database Performance Issues
1. **Complex Movement Queries**: Storereport table can be very large
2. **Individual Product Analysis**: Each product analyzed separately
3. **Source Document Validation**: Additional queries to validate each movement
4. **Multi-Store Queries**: Complex permission-based filtering

### Optimization Recommendations
1. **Indexing**:
   ```sql
   -- Critical indexes for performance
   CREATE INDEX idx_storereport_product_date ON storereport(productid, storereportdate);
   CREATE INDEX idx_storereport_store_date ON storereport(storeid, storereportdate);
   CREATE INDEX idx_sellbill_conditions ON sellbill(sellbillid, conditions);
   CREATE INDEX idx_buybill_conditions ON buybill(buybillid, conditions);
   ```

2. **Query Optimization**: Batch movement validation instead of individual checks
3. **Caching**: Cache category hierarchy and unit conversion factors

### Memory Considerations
- Large product categories can generate extensive movement data
- Individual product analysis can be memory-intensive
- Consider pagination for categories with many products

---

## ๐Ÿ› Common Issues & Troubleshooting

### 1. **Inconsistent Movement Data**
**Issue**: Movement totals don't match transaction records  
**Cause**: Storereport table out of sync with actual transactions

**Debug**:
sql

-- Check for orphaned storereport records

SELECT sr.* FROM storereport sr

LEFT JOIN sellbill sb ON sr.storereportmodelid = sb.sellbillid

AND sr.tablename = 'sellbillController.php'

LEFT JOIN buybill bb ON sr.storereportmodelid = bb.buybillid

AND sr.tablename = 'buyBillController.php'

WHERE (sr.tablename = 'sellbillController.php' AND sb.sellbillid IS NULL)

OR (sr.tablename = 'buyBillController.php' AND bb.buybillid IS NULL);

-- Check for deleted transactions affecting movements

SELECT COUNT(*) FROM storereport sr

JOIN sellbill sb ON sr.storereportmodelid = sb.sellbillid

WHERE sr.tablename = 'sellbillController.php' AND sb.conditions != 0;

### 2. **Store Permission Issues**
**Issue**: Users see wrong store data or empty results  
**Cause**: Session store permissions not properly set

**Debug**:
php

// Add debugging

error_log("User store permissions: searchinonestore=" . $_SESSION['searchinonestore'] .

", storeid=" . $_SESSION['storeid'] .

", storeids=" . $_SESSION['storeids']);

### 3. **Unit Conversion Errors**
**Issue**: Quantities don't match expected values  
**Cause**: Missing or incorrect product unit data

**Fix**: Ensure all products have proper unit assignments:
sql

-- Find products without unit data

SELECT p.productid, p.productname

FROM product p

LEFT JOIN productunit pu ON p.productid = pu.productid

WHERE pu.productunitid IS NULL;

---

## ๐Ÿงช Testing Scenarios

### Test Case 1: Movement Validation

1. Create sales bill and verify storereport entry created

2. Delete/mark bill as deleted (conditions = 1)

3. Run movement report

4. Verify movement shows as 0 (nullified)

5. Test with purchase bills and returns

### Test Case 2: Multi-Store Analysis

1. Set up user with multiple store access

2. Create movements in different stores

3. Filter by specific store

4. Verify only movements from selected store appear

5. Test store permission enforcement

### Test Case 3: Category Movement Analysis

1. Create products in nested categories

2. Create various transaction types for products

3. Run report for parent category

4. Verify all child product movements included

5. Check movement type classification (in/out)

### Test Case 4: Profit Calculation Accuracy

1. Create sales with known buy/sell prices

2. Apply various discount types

3. Use different product units

4. Verify profit calculations match manual calculations

5. Test with returns and adjustments

```

---

๐Ÿ“š Related Documentation

---

Documented By: AI Assistant

Review Status: โœ… Complete

Next Review: When major changes occur