requireLogin(); $page_title = 'Sales Management'; $success_message = ''; $error_message = ''; // Get edit data if editing $edit_sale = null; $edit_sale_items = []; if (isset($_GET['edit_sale'])) { try { $edit_stmt = $pdo->prepare("SELECT * FROM sales WHERE id = ?"); $edit_stmt->execute([$_GET['edit_sale']]); $edit_sale = $edit_stmt->fetch(PDO::FETCH_ASSOC); if ($edit_sale) { $edit_items_stmt = $pdo->prepare(" SELECT si.*, ps.name as product_name FROM sales_items si JOIN products_services ps ON si.product_service_id = ps.id WHERE si.sale_id = ? "); $edit_items_stmt->execute([$edit_sale['id']]); $edit_sale_items = $edit_items_stmt->fetchAll(PDO::FETCH_ASSOC); } } catch (PDOException $e) { $error_message = 'Error fetching sale for editing.'; } } // Handle form submissions if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) { if ($_POST['action'] == 'record_sale' || $_POST['action'] == 'edit_sale') { $sale_id = $_POST['sale_id'] ?? null; $sale_date = $_POST['sale_date'] ?? ''; $customer_name = trim($_POST['customer_name'] ?? ''); $customer_phone = trim($_POST['customer_phone'] ?? ''); $payment_status = $_POST['payment_status'] ?? 'paid'; $payment_method = $_POST['payment_method'] ?? 'cash'; $notes = trim($_POST['notes'] ?? ''); // Get line items from individual form fields $products = $_POST['product_id'] ?? []; $quantities = $_POST['quantity'] ?? []; $unit_prices = $_POST['unit_price'] ?? []; $line_totals = $_POST['line_total'] ?? []; // Validation if (empty($sale_date)) { $error_message = 'Sale date is required.'; } elseif (empty($products) || !is_array($products)) { $error_message = 'At least one product/service must be added.'; } else { // Build line items array $line_items = []; $total_amount = 0; for ($i = 0; $i < count($products); $i++) { if (!empty($products[$i]) && !empty($quantities[$i]) && isset($unit_prices[$i]) && isset($line_totals[$i])) { $product_id = intval($products[$i]); $quantity = floatval($quantities[$i]); $unit_price = floatval($unit_prices[$i]); $line_total = floatval($line_totals[$i]); if ($product_id > 0 && $quantity > 0 && $unit_price >= 0 && $line_total >= 0) { $line_items[] = [ 'product_id' => $product_id, 'quantity' => $quantity, 'unit_price' => $unit_price, 'line_total' => $line_total ]; $total_amount += $line_total; } } } if (empty($line_items)) { $error_message = 'At least one valid product/service must be added.'; } elseif ($total_amount <= 0) { $error_message = 'Total amount must be greater than zero.'; } else { try { // Start transaction $pdo->beginTransaction(); if ($_POST['action'] == 'edit_sale' && $sale_id) { // Update existing sale $sale_stmt = $pdo->prepare(" UPDATE sales SET sale_date = ?, customer_name = ?, customer_phone = ?, total_amount = ?, payment_status = ?, payment_method = ?, notes = ? WHERE id = ? "); $sale_stmt->execute([ $sale_date, $customer_name, $customer_phone, $total_amount, $payment_status, $payment_method, $notes, $sale_id ]); // Delete existing line items $delete_items_stmt = $pdo->prepare("DELETE FROM sales_items WHERE sale_id = ?"); $delete_items_stmt->execute([$sale_id]); $action_message = 'Sale updated successfully!'; } else { // Create new sale $bill_number = 'BILL' . date('YmdHis') . rand(100, 999); $sale_stmt = $pdo->prepare(" INSERT INTO sales (bill_number, sale_date, customer_name, customer_phone, total_amount, payment_status, payment_method, notes, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) "); $sale_stmt->execute([ $bill_number, $sale_date, $customer_name, $customer_phone, $total_amount, $payment_status, $payment_method, $notes, $_SESSION['user_id'] ]); $sale_id = $pdo->lastInsertId(); $action_message = 'Sale recorded successfully! Bill: ' . $bill_number; } // Insert line items $item_stmt = $pdo->prepare(" INSERT INTO sales_items (sale_id, product_service_id, quantity, unit_price, line_total) VALUES (?, ?, ?, ?, ?) "); foreach ($line_items as $item) { $item_stmt->execute([ $sale_id, $item['product_id'], $item['quantity'], $item['unit_price'], $item['line_total'] ]); } $pdo->commit(); $_SESSION['success_message'] = $action_message; // Redirect to prevent form resubmission header("Location: sales.php"); exit(); } catch (Exception $e) { $pdo->rollBack(); $error_message = 'Error processing sale: ' . $e->getMessage(); } } } } elseif ($_POST['action'] == 'delete_sale') { $sale_id = intval($_POST['sale_id'] ?? 0); if ($sale_id > 0) { try { $pdo->beginTransaction(); // Delete sales items first $delete_items_stmt = $pdo->prepare("DELETE FROM sales_items WHERE sale_id = ?"); $delete_items_stmt->execute([$sale_id]); // Delete sale $delete_sale_stmt = $pdo->prepare("DELETE FROM sales WHERE id = ?"); $result = $delete_sale_stmt->execute([$sale_id]); if ($result) { $pdo->commit(); $_SESSION['success_message'] = 'Sale deleted successfully.'; } else { $pdo->rollBack(); $_SESSION['error_message'] = 'Failed to delete sale.'; } } catch (Exception $e) { $pdo->rollBack(); $_SESSION['error_message'] = 'Error deleting sale: ' . $e->getMessage(); } } header("Location: sales.php"); exit(); } elseif ($_POST['action'] == 'export_sales') { $export_detailed = $_POST['export_detailed'] ?? '0'; $export_dashboard = $_POST['export_dashboard'] ?? '0'; $export_filter_date_from = $_POST['export_filter_date_from'] ?? ''; $export_filter_date_to = $_POST['export_filter_date_to'] ?? ''; $export_filter_product = $_POST['export_filter_product'] ?? ''; // Build export query with same filters $export_where_clause = "1=1"; $export_params = []; if (!empty($export_filter_date_from)) { $export_where_clause .= " AND s.sale_date >= ?"; $export_params[] = $export_filter_date_from; } if (!empty($export_filter_date_to)) { $export_where_clause .= " AND s.sale_date <= ?"; $export_params[] = $export_filter_date_to; } if (!empty($export_filter_product)) { $export_where_clause .= " AND EXISTS (SELECT 1 FROM sales_items si WHERE si.sale_id = s.id AND si.product_service_id = ?)"; $export_params[] = $export_filter_product; } try { if ($export_dashboard == '1') { // Export dashboard - product-wise summary $export_stmt = $pdo->prepare(" SELECT ps.name as product_name, un.unit_symbol, COUNT(DISTINCT s.id) as total_bills, SUM(si.quantity) as total_quantity, AVG(si.unit_price) as avg_unit_price, SUM(si.line_total) as total_sales_amount FROM sales s JOIN sales_items si ON s.id = si.sale_id JOIN products_services ps ON si.product_service_id = ps.id JOIN units un ON ps.unit_id = un.id WHERE $export_where_clause GROUP BY ps.id, ps.name, un.unit_symbol ORDER BY total_sales_amount DESC "); $export_stmt->execute($export_params); $export_data = $export_stmt->fetchAll(PDO::FETCH_ASSOC); // Get totals $totals_stmt = $pdo->prepare(" SELECT COUNT(DISTINCT s.id) as total_bills, SUM(s.total_amount) as grand_total FROM sales s WHERE $export_where_clause "); $totals_stmt->execute($export_params); $totals = $totals_stmt->fetch(PDO::FETCH_ASSOC); $filename = 'sales_dashboard_' . date('Y-m-d_H-i-s') . '.csv'; // Generate CSV header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="' . $filename . '"'); $output = fopen('php://output', 'w'); // Dashboard header fputcsv($output, ['SALES DASHBOARD REPORT']); fputcsv($output, ['Generated on: ' . date('F j, Y g:i A')]); if (!empty($export_filter_date_from) || !empty($export_filter_date_to)) { $period = 'Period: '; if (!empty($export_filter_date_from)) $period .= 'From ' . date('M j, Y', strtotime($export_filter_date_from)); if (!empty($export_filter_date_to)) $period .= ' To ' . date('M j, Y', strtotime($export_filter_date_to)); fputcsv($output, [$period]); } if (!empty($export_filter_product)) { $product_name = ''; foreach ($products_services as $ps) { if ($ps['id'] == $export_filter_product) { $product_name = $ps['name']; break; } } fputcsv($output, ['Product Filter: ' . $product_name]); } fputcsv($output, ['']); // Empty row // Summary totals fputcsv($output, ['SUMMARY']); fputcsv($output, ['Total Bills', $totals['total_bills']]); fputcsv($output, ['Grand Total Sales', number_format($totals['grand_total'], 2)]); fputcsv($output, ['']); // Empty row // Product-wise details fputcsv($output, ['PRODUCT-WISE SALES BREAKDOWN']); fputcsv($output, ['Product Name', 'Unit', 'Total Bills', 'Total Quantity', 'Avg Unit Price', 'Total Sales Amount']); foreach ($export_data as $row) { fputcsv($output, [ $row['product_name'], $row['unit_symbol'], $row['total_bills'], number_format($row['total_quantity'], 3), number_format($row['avg_unit_price'], 2), number_format($row['total_sales_amount'], 2) ]); } fclose($output); exit(); } elseif ($export_detailed == '1') { // Export detailed view $export_stmt = $pdo->prepare(" SELECT s.bill_number, s.sale_date, s.customer_name, s.customer_phone, ps.name as product_name, si.quantity, un.unit_symbol, si.unit_price, si.line_total, s.payment_status, s.payment_method, s.notes FROM sales s JOIN sales_items si ON s.id = si.sale_id JOIN products_services ps ON si.product_service_id = ps.id JOIN units un ON ps.unit_id = un.id WHERE $export_where_clause ORDER BY s.created_at DESC, s.id, si.id "); $export_stmt->execute($export_params); $export_data = $export_stmt->fetchAll(PDO::FETCH_ASSOC); $filename = 'sales_detailed_' . date('Y-m-d_H-i-s') . '.csv'; $headers = ['Bill Number', 'Date', 'Customer Name', 'Customer Phone', 'Product', 'Quantity', 'Unit', 'Unit Price', 'Line Total', 'Payment Status', 'Payment Method', 'Notes']; } else { // Export summary view $export_stmt = $pdo->prepare(" SELECT s.bill_number, s.sale_date, s.customer_name, s.customer_phone, s.total_amount, s.payment_status, s.payment_method, s.notes, GROUP_CONCAT(CONCAT(ps.name, ' (', si.quantity, ' ', un.unit_symbol, ')') SEPARATOR ', ') as items FROM sales s LEFT JOIN sales_items si ON s.id = si.sale_id LEFT JOIN products_services ps ON si.product_service_id = ps.id LEFT JOIN units un ON ps.unit_id = un.id WHERE $export_where_clause GROUP BY s.id ORDER BY s.created_at DESC "); $export_stmt->execute($export_params); $export_data = $export_stmt->fetchAll(PDO::FETCH_ASSOC); $filename = 'sales_summary_' . date('Y-m-d_H-i-s') . '.csv'; $headers = ['Bill Number', 'Date', 'Customer Name', 'Customer Phone', 'Items', 'Total Amount', 'Payment Status', 'Payment Method', 'Notes']; } if ($export_dashboard != '1') { // Generate CSV for detailed/summary views header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="' . $filename . '"'); $output = fopen('php://output', 'w'); fputcsv($output, $headers); foreach ($export_data as $row) { if ($export_detailed == '1') { fputcsv($output, [ $row['bill_number'], $row['sale_date'], $row['customer_name'], $row['customer_phone'], $row['product_name'], $row['quantity'], $row['unit_symbol'], $row['unit_price'], $row['line_total'], $row['payment_status'], $row['payment_method'], $row['notes'] ]); } else { fputcsv($output, [ $row['bill_number'], $row['sale_date'], $row['customer_name'], $row['customer_phone'], $row['items'], $row['total_amount'], $row['payment_status'], $row['payment_method'], $row['notes'] ]); } } fclose($output); exit(); } } catch (PDOException $e) { $error_message = 'Error exporting data: ' . $e->getMessage(); } } } // Fetch products/services try { $products_stmt = $pdo->prepare(" SELECT ps.*, u.unit_name, u.unit_symbol FROM products_services ps JOIN units u ON ps.unit_id = u.id WHERE ps.status = 'active' ORDER BY ps.name "); $products_stmt->execute(); $products_services = $products_stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { $products_services = []; } // Fetch sales with filters $filter_date_from = $_GET['filter_date_from'] ?? ''; $filter_date_to = $_GET['filter_date_to'] ?? ''; $filter_product = $_GET['filter_product'] ?? ''; $detailed_view = $_GET['detailed_view'] ?? '0'; $where_clause = "1=1"; $params = []; if (!empty($filter_date_from)) { $where_clause .= " AND s.sale_date >= ?"; $params[] = $filter_date_from; } if (!empty($filter_date_to)) { $where_clause .= " AND s.sale_date <= ?"; $params[] = $filter_date_to; } if (!empty($filter_product)) { $where_clause .= " AND EXISTS (SELECT 1 FROM sales_items si WHERE si.sale_id = s.id AND si.product_service_id = ?)"; $params[] = $filter_product; } try { // Get sales stats $stats_stmt = $pdo->prepare(" SELECT COUNT(*) as total_sales, COALESCE(SUM(total_amount), 0) as total_revenue, COALESCE(SUM(CASE WHEN payment_status = 'paid' THEN total_amount ELSE 0 END), 0) as paid_amount FROM sales s WHERE $where_clause "); $stats_stmt->execute($params); $stats = $stats_stmt->fetch(PDO::FETCH_ASSOC); if ($detailed_view == '1') { // Get detailed view - each line item as separate row $sales_stmt = $pdo->prepare(" SELECT s.*, u.full_name as created_by_name, si.quantity, si.unit_price, si.line_total, ps.name as product_name, un.unit_symbol FROM sales s JOIN users u ON s.created_by = u.id JOIN sales_items si ON s.id = si.sale_id JOIN products_services ps ON si.product_service_id = ps.id JOIN units un ON ps.unit_id = un.id WHERE $where_clause ORDER BY s.created_at DESC, s.id, si.id LIMIT 200 "); $sales_stmt->execute($params); $sales = $sales_stmt->fetchAll(PDO::FETCH_ASSOC); } else { // Get sales records with items (summary view) $sales_stmt = $pdo->prepare(" SELECT s.*, u.full_name as created_by_name, (SELECT GROUP_CONCAT( CONCAT(ps.name, ': ', si.quantity, ' ', u.unit_symbol, ' @ ₹', si.unit_price, ' = ₹', si.line_total) SEPARATOR '|' ) FROM sales_items si JOIN products_services ps ON si.product_service_id = ps.id JOIN units u ON ps.unit_id = u.id WHERE si.sale_id = s.id) as items_summary FROM sales s JOIN users u ON s.created_by = u.id WHERE $where_clause ORDER BY s.created_at DESC LIMIT 50 "); $sales_stmt->execute($params); $sales = $sales_stmt->fetchAll(PDO::FETCH_ASSOC); } } catch (PDOException $e) { $stats = ['total_sales' => 0, 'total_revenue' => 0, 'paid_amount' => 0]; $sales = []; $error_message = 'Error fetching sales data.'; } include 'includes/header.php'; ?>
Total Sales
Total Revenue
Paid Amount

Please create products or services first.

Products/Services

$item): ?>

Grand Total: ₹0.00

Cancel

Filter & Export Sales

Clear Filters

Sales Records

$filter_date_from, 'filter_date_to' => $filter_date_to, 'filter_product' => $filter_product ]); ?> Summary View Detailed View

No Sales Found

Record your first sale using the form above.

Bill# Date Product Quantity Unit Price Line Total Items Total Customer Payment Actions
' . htmlspecialchars($item) . ''; } } else { echo '
No items
'; } ?>

-

Edit
Notes: