الجزء 8: المبيعات وعناصر المبيعات

Amine
20/09/2024

تعد وحدات المبيعات وعناصر المبيعات أساسية في نظام نقاط البيع، حيث تقوم بتسجيل المعاملات وربطها بالمنتجات والعملاء. سنقوم بتنفيذ الوظائف التالية:

  • عرض قائمة بجميع المبيعات
  • إنشاء عملية بيع جديدة
  • عرض تفاصيل عملية البيع، بما في ذلك عناصر البيع
  • إضافة وإزالة عناصر البيع ديناميكيًا باستخدام jQuery
  • تحديث مخزون المنتجات بعد كل عملية بيع

عرض المبيعات (sales/index.php)

هذه الصفحة تعرض قائمة المبيعات، بما في ذلك اسم العميل، المبلغ الإجمالي، وتاريخ البيع:

<?php
// sales/index.php
include '../includes/header.php';
include '../includes/db_connect.php';

// جلب جميع المبيعات مع أسماء العملاء
$stmt = $pdo->query('
    SELECT sales.*, customers.name AS customer_name
    FROM sales
    JOIN customers ON sales.customer_id = customers.id
    ORDER BY sales.sale_date DESC
');
$sales = $stmt->fetchAll();
?>

<div class="flex justify-between mb-4">
    <h2 class="text-2xl font-semibold">Sales</h2>
    <a href="create.php" class="bg-green-500 text-white px-4 py-2 rounded">New Sale</a>
</div>

<table class="min-w-full bg-white">
    <thead>
        <tr>
            <th class="py-2">ID</th>
            <th class="py-2">Customer</th>
            <th class="py-2">Date</th>
            <th class="py-2">Total Amount</th>
            <th class="py-2">Actions</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($sales as $sale): ?>
            <tr class="text-center border-t">
                <td class="py-2"><?php echo htmlspecialchars($sale['id']); ?></td>
                <td class="py-2"><?php echo htmlspecialchars($sale['customer_name']); ?></td>
                <td class="py-2"><?php echo htmlspecialchars($sale['sale_date']); ?></td>
                <td class="py-2">$<?php echo htmlspecialchars($sale['total_amount']); ?></td>
                <td class="py-2">
                    <a href="view.php?id=<?php echo $sale['id']; ?>" class="bg-blue-500 text-white px-2 py-1 rounded">View</a>
                </td>
            </tr>
        <?php endforeach; ?>
    </tbody>
</table>

<?php include '../includes/footer.php'; ?>

إنشاء عملية بيع جديدة (sales/create.php)

إنشاء عملية بيع يتطلب اختيار العميل، إضافة المنتجات، تحديد الكميات، وحساب الإجماليات. سيتم استخدام jQuery لإضافة عناصر البيع ديناميكيًا وتحديث الإجماليات.

<?php
// sales/create.php
include '../includes/header.php';
include '../includes/db_connect.php';

// جلب العملاء للاختيار من القائمة المنسدلة
$stmt = $pdo->query('SELECT * FROM customers');
$customers = $stmt->fetchAll();

// جلب المنتجات للاختيار من القائمة المنسدلة
$stmt = $pdo->query('SELECT * FROM products');
$products = $stmt->fetchAll();

$customer_id = '';
$sale_items = [];
$error = '';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $customer_id = $_POST['customer_id'];
    $product_ids = $_POST['product_id'];
    $quantities = $_POST['quantity'];

    if (empty($customer_id)) {
        $error = 'Customer is required.';
    } elseif (empty($product_ids) || empty($quantities)) {
        $error = 'At least one product must be selected.';
    } else {
        // بدء المعاملة
        $pdo->beginTransaction();
        try {
            // حساب المبلغ الإجمالي
            $total_amount = 0;
            $items = [];
            for ($i = 0; $i < count($product_ids); $i++) {
                $prod_id = $product_ids[$i];
                $qty = $quantities[$i];

                if ($qty <= 0) continue;

                // جلب سعر المنتج والمخزون
                $stmt = $pdo->prepare('SELECT price, stock FROM products WHERE id = :id');
                $stmt->execute(['id' => $prod_id]);
                $product = $stmt->fetch();

                if (!$product) {
                    throw new Exception('Product not found.');
                }

                if ($product['stock'] < $qty) {
                    throw new Exception('Insufficient stock for product ID ' . $prod_id);
                }

                $unit_price = $product['price'];
                $total_price = $unit_price * $qty;
                $total_amount += $total_price;

                // تقليل المخزون
                $stmt = $pdo->prepare('UPDATE products SET stock = stock - :qty WHERE id = :id');
                $stmt->execute(['qty' => $qty, 'id' => $prod_id]);

                $items[] = [
                    'product_id' => $prod_id,
                    'quantity' => $qty,
                    'unit_price' => $unit_price,
                    'total_price' => $total_price
                ];
            }

            if (empty($items)) {
                throw new Exception('No valid sale items.');
            }

            // إدراج البيانات في جدول المبيعات
            $stmt = $pdo->prepare('INSERT INTO sales (customer_id, total_amount) VALUES (:customer_id, :total_amount)');
            $stmt->execute(['customer_id' => $customer_id, 'total_amount' => $total_amount]);
            $sale_id = $pdo->lastInsertId();

            // إدراج البيانات في جدول عناصر المبيعات
            $stmt = $pdo->prepare('INSERT INTO sale_items (sale_id, product_id, quantity, unit_price, total_price) VALUES (:sale_id, :product_id, :quantity, :unit_price, :total_price)');
            foreach ($items as $item) {
                $stmt->execute([
                    'sale_id' => $sale_id,
                    'product_id' => $item['product_id'],
                    'quantity' => $item['quantity'],
                    'unit_price' => $item['unit_price'],
                    'total_price' => $item['total_price']
                ]);
            }

            // تأكيد المعاملة
            $pdo->commit();
            header('Location: index.php');
            exit();
        } catch (Exception $e) {
            $pdo->rollBack();
            $error = $e->getMessage();
        }
    }
}
?>

<div class="max-w-4xl mx-auto bg-white p-6 rounded shadow">
    <h2 class="text-2xl font-semibold mb-4">New Sale</h2>
    <?php if ($error): ?>
        <div class="bg-red-100 text-red-700 p-2 mb-4 rounded">
            <?php echo htmlspecialchars($error); ?>
        </div>
    <?php endif; ?>
    <form method="POST" action="">
        <div class="mb-4">
            <label class="block text-gray-700">Customer</label>
            <select name="customer_id" class="w-full px-3 py-2 border rounded" required>
                <option value="">Select Customer</option>
                <?php foreach ($customers as $customer): ?>
                    <option value="<?php echo $customer['id']; ?>" <?php if ($customer['id'] == $customer_id) echo 'selected'; ?>>
                        <?php echo htmlspecialchars($customer['name']); ?>
                    </option>
                <?php endforeach; ?>
            </select>
        </div>

        <h3 class="text-xl font-semibold mb-2">Products</h3>
        <table class="w-full mb-4">
            <thead>
                <tr>
                    <th class="py-2">Product</th>
                    <th class="py-2">Price</th>
                    <th class="py-2">Quantity</th>
                    <th class="py-2">Total</th>
                    <th class="py-2">Actions</th>
                </tr>
            </thead>
            <tbody id="sale-items">
                <tr class="sale-item">
                    <td>
                        <select name="product_id[]" class="w-full px-2 py-1 border rounded product-select" required>
                            <option value="">Select Product</option>
                            <?php foreach ($products as $product): ?>
                                <option value="<?php echo $product['id']; ?>">
                                    <?php echo htmlspecialchars($product['name']); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                    </td>
                    <td class="price">$0.00</td>
                    <td>
                        <input type="number" name="quantity[]" class="w-full px-2 py-1 border rounded quantity-input" min="1" value="1" required>
                    </td>
                    <td class="total">$0.00</td>
                    <td>
                        <button type="button" class="remove-item bg-red-500 text-white px-2 py-1 rounded">Remove</button>
                    </td>
                </tr>
            </tbody>
        </table>
        <button type="button" id="add-item" class="bg-gray-500 text-white px-4 py-2 rounded mb-4">Add Product</button>

        <div class="flex justify-end mb-4">
            <span class="text-xl font-semibold">Total: $<span id="grand-total">0.00</span></span>
        </div>

        <div class="flex justify-end">
            <button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded">Complete Sale</button>
        </div>
    </form>
</div>

<script>
    $(document).ready(function() {
        // Fetch product price on product selection
        $(document).on('change', '.product-select', function() {
            var productId = $(this).val();
            var row = $(this).closest('tr');
            if (productId) {
                $.ajax({
                    url: '/pos_system/products/get_price.php',
                    method: 'POST',
                    data: { id: productId },
                    dataType: 'json',
                    success: function(data) {
                        if (data.success) {
                            row.find('.price').text('$' + parseFloat(data.price).toFixed(2));
                            var quantity = row.find('.quantity-input').val();
                            var total = parseFloat(data.price) * parseInt(quantity);
                            row.find('.total').text('$' + total.toFixed(2));
                            updateGrandTotal();
                        } else {
                            row.find('.price').text('$0.00');
                            row.find('.total').text('$0.00');
                            updateGrandTotal();
                        }
                    }
                });
            } else {
                row.find('.price').text('$0.00');
                row.find('.total').text('$0.00');
                updateGrandTotal();
            }
        });

        // Update total on quantity change
        $(document).on('input', '.quantity-input', function() {
            var quantity = $(this).val();
            var row = $(this).closest('tr');
            var priceText = row.find('.price').text().replace('$', '');
            var price = parseFloat(priceText) || 0;
            var total = price * parseInt(quantity);
            row.find('.total').text('$' + (isNaN(total) ? '0.00' : total.toFixed(2)));
            updateGrandTotal();
        });

        // Add new sale item row
        $('#add-item').click(function() {
            var newRow = `
                <tr class="sale-item">
                    <td>
                        <select name="product_id[]" class="w-full px-2 py-1 border rounded product-select" required>
                            <option value="">Select Product</option>
                            <?php foreach ($products as $product): ?>
                                <option value="<?php echo $product['id']; ?>">
                                    <?php echo htmlspecialchars($product['name']); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                    </td>
                    <td class="price">$0.00</td>
                    <td>
                        <input type="number" name="quantity[]" class="w-full px-2 py-1 border rounded quantity-input" min="1" value="1" required>
                    </td>
                    <td class="total">$0.00</td>
                    <td>
                        <button type="button" class="remove-item bg-red-500 text-white px-2 py-1 rounded">Remove</button>
                    </td>
                </tr>
            `;
            $('#sale-items').append(newRow);
        });

        // Remove sale item row
        $(document).on('click', '.remove-item', function() {
            $(this).closest('tr').remove();
            updateGrandTotal();
        });

        // Update grand total
        function updateGrandTotal() {
            var grandTotal = 0;
            $('.total').each(function() {
                var total = parseFloat($(this).text().replace('$', '')) || 0;
                grandTotal += total;
            });
            $('#grand-total').text(grandTotal.toFixed(2));
        }
    });
</script>

<?php include '../includes/footer.php'; ?>

عرض تفاصيل عملية البيع (sales/view.php)

عند اكتمال عملية البيع، تعرض هذه الصفحة تفاصيل البيع، بما في ذلك العميل، المنتجات المباعة، الكميات، والمبالغ الإجمالية.

<?php
// sales/view.php
include '../includes/header.php';
include '../includes/db_connect.php';

if (!isset($_GET['id'])) {
    header('Location: index.php');
    exit();
}

$sale_id = $_GET['id'];

// جلب تفاصيل البيع
$stmt = $pdo->prepare('
    SELECT sales.*, customers.name AS customer_name, customers.email, customers.phone, customers.address
    FROM sales
    JOIN customers ON sales.customer_id = customers.id
    WHERE sales.id = :id
');
$stmt->execute(['id' => $sale_id]);
$sale = $stmt->fetch();

if (!$sale) {
    echo "Sale not found.";
    exit();
}

// جلب عناصر البيع
$stmt = $pdo->prepare('
    SELECT sale_items.*, products.name AS product_name
    FROM sale_items
    JOIN products ON sale_items.product_id = products.id
    WHERE sale_items.sale_id = :sale_id
');
$stmt->execute(['sale_id' => $sale_id]);
$sale_items = $stmt->fetchAll();
?>

<div class="max-w-4xl mx-auto bg-white p-6 rounded shadow mb-4">
    <h2 class="text-2xl font-semibold mb-4">Sale Details</h2>
    <div class="mb-4">
        <strong>Sale ID:</strong> <?php echo htmlspecialchars($sale['id']); ?><br>
        <strong>Date:</strong> <?php echo htmlspecialchars($sale['sale_date']); ?><br>
        <strong>Customer:</strong> <?php echo htmlspecialchars($sale['customer_name']); ?><br>
        <strong>Email:</strong> <?php echo htmlspecialchars($sale['email']); ?><br>
        <strong>Phone:</strong> <?php echo htmlspecialchars($sale['phone']); ?><br>
        <strong>Address:</strong> <?php echo htmlspecialchars($sale['address']); ?><br>
    </div>
    <h3 class="text-xl font-semibold mb-2">Items</h3>
    <table class="w-full mb-4">
        <thead>
            <tr>
                <th class="py-2">Product</th>
                <th class="py-2">Quantity</th>
                <th class="py-2">Unit Price</th>
                <th class="py-2">Total Price</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($sale_items as $item): ?>
                <tr class="text-center border-t">
                    <td class="py-2"><?php echo htmlspecialchars($item['product_name']); ?></td>
                    <td class="py-2"><?php echo htmlspecialchars($item['quantity']); ?></td>
                    <td class="py-2">$<?php echo htmlspecialchars($item['unit_price']); ?></td>
                    <td class="py-2">$<?php echo htmlspecialchars($item['total_price']); ?></td>
                </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
    <div class="flex justify-end">
        <span class="text-xl font-semibold">Total Amount: $<?php echo htmlspecialchars($sale['total_amount']); ?></span>
    </div>
</div>

<div class="flex justify-end">
    <a href="index.php" class="bg-blue-500 text-white px-4 py-2 rounded">Back to Sales</a>
</div>

<?php include '../includes/footer.php'; ?>

الخطوات القادمة

بعد إكمال إدارة المبيعات وعناصر المبيعات، يمكنك الآن إدارة المعاملات في نظام نقاط البيع بكفاءة، مع إمكانية عرض تفاصيل كل عملية بيع وتحديث المخزون بعد كل عملية.

التعليقات

اترك تعليقاً