JIT
fixing order list not clearing after payment in php code
Here is the problem. Pasted below is my Order menu code, theproblem is that whenever a transaction finishes, meaning the order have been change its status to "Paid" apparantly it should clear the, ready for another order, Order List after click "Pay". Now, the problem is that, that list orders even though the transaction is finished, it stays there. Here is the code: <?php // IMPORTANT: session_start() MUST be the very first thing in the file, // before any output (including whitespace outside of <?php tags). // This is necessary if your login system relies on raw PHP sessions ($_SESSION). // In a standard WordPress setup, you would typically rely on WP's authentication // functions like wp_get_current_user() and is_user_logged_in(). // If wp_get_current_user() is not working, the issue might be how this file // is included or executed within the WordPress lifecycle. session_start(); ?> <?php global $wpdb; // Define your tables $table_name_categories = 'categories'; $table_name_products = 'products'; $table_name_orders = 'orders'; $table_name_order_items = 'order_items'; $inventory_history_table_name = 'inventory_history'; $message = ''; // Function to log inventory actions function log_inventory_action($action_type, $item_type, $item_name, $description) { global $wpdb; $username = wp_get_current_user()->user_login; $result = $wpdb->insert( 'inventory_history', array( 'username' => $username, 'action_type' => $action_type, 'item_type' => $item_type, 'item_name' => $item_name, 'description' => $description, 'timestamp' => current_time('mysql') ), array('%s', '%s', '%s', '%s', '%s', '%s') ); return $result; } // --- Fetch Categories --- $categories = $wpdb->get_results("SELECT * FROM $table_name_categories ORDER BY name ASC", ARRAY_A); // --- Handle Add Order Form Submission --- if (isset($_POST['add_order']) && isset($_POST['_wpnonce_order_form']) && wp_verify_nonce($_POST['_wpnonce_order_form'], 'add_order_action')) { $product_name = isset($_POST['product_name']) ? sanitize_text_field($_POST['product_name']) : ''; $quantity = isset($_POST['quantity']) ? absint($_POST['quantity']) : 0; $unit_price = isset($_POST['unit_price']) ? floatval($_POST['unit_price']) : 0.00; if (empty($product_name) || !$quantity || !$unit_price) { $message = 'Invalid product name, quantity, or unit price!'; } else { $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name_products WHERE name = %s", $product_name)); if ($product) { if ($quantity <= $product->{'in_stock'}) { $amount = $unit_price * $quantity; // Insert into orders table $wpdb->insert($table_name_orders, [ 'customer_name' => 'Default Customer', 'total_amount' => $amount, 'status' => 'Pending', 'created_at' => current_time('mysql') ]); $order_id = $wpdb->insert_id; // Insert the order item $wpdb->insert($table_name_order_items, [ 'order_id' => $order_id, 'product_id' => $product->id, 'quantity' => $quantity, 'unit_price' => $unit_price, 'amount' => $amount ]); // Update product stock $new_stock = $product->{'in_stock'} - $quantity; $wpdb->update($table_name_products, ['in_stock' => $new_stock], ['id' => $product->id]); // Log the sale in inventory history log_inventory_action( 'sell', 'product', $product_name, "Sold {$quantity} units of {$product_name}. New stock: {$new_stock}" ); wp_redirect($_SERVER['REQUEST_URI']); exit; } else { $message = 'Insufficient stock for the product!'; } } else { $message = 'Product not found!'; } } } // --- Handle Payment and Cancellation of Orders --- if (isset($_POST['pay_orders'])) { // Mark all orders as paid $wpdb->update($table_name_orders, ['status' => 'Paid'], ['status' => 'Pending']); // Log the payment action log_inventory_action( 'update', 'order', 'Multiple Products', "Marked all pending orders as paid" ); wp_redirect($_SERVER['REQUEST_URI']); exit; } if (isset($_POST['cancel_order'])) { $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0; if ($order_id) { $order_items = $wpdb->get_results($wpdb->prepare("SELECT oi.*, p.name AS product_name FROM $table_name_order_items oi JOIN $table_name_products p ON oi.product_id = p.id WHERE order_id = %d", $order_id), ARRAY_A); foreach ($order_items as $item) { $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name_products WHERE id = %d", $item['product_id'])); if ($product) { $currentStock = $product->{'in_stock'}; $restoredStock = $currentStock + $item['quantity']; $wpdb->update($table_name_products, ['in_stock' => $restoredStock], ['id' => $product->id]); // Log the stock restoration log_inventory_action( 'update', 'product', $item['product_name'], "Restored {$item['quantity']} units of {$item['product_name']} from cancelled order #{$order_id}. New stock: {$restoredStock}" ); } } // Log the order cancellation log_inventory_action( 'delete', 'order', "Order #{$order_id}", "Cancelled order #{$order_id} and restored product stock" ); $wpdb->delete($table_name_orders, ['id' => $order_id]); $wpdb->delete($table_name_order_items, ['order_id' => $order_id]); wp_redirect($_SERVER['REQUEST_URI']); exit; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Orders Management</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; } .dashboard-container { display: flex; } .sidebar { width: 240px; background-color: #D1B48C; color: #000000; height: 100vh; position: fixed; left: 0; top: 0; overflow-y: auto; box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); z-index: 1000; } .sidebar-header { padding: 20px; text-align: center; } .sidebar-header .inventory-name { font-size: 17px; font-weight: bold; color: #000; } .sidebar-menu { padding: 20px 0; } .sidebar-menu ul { list-style: none; padding: 0; margin: 0; } .sidebar-menu li a { display: flex; align-items: center; padding: 12px 20px; text-decoration: none; color: #000000; transition: background-color 0.3s ease, color 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 16px; } .sidebar-menu li a i { margin-right: 12px; width: 20px; text-align: center; flex-shrink: 0; } .sidebar-menu li a:hover { background-color: #ffffff; color: #000000; } .main-content { margin-left: 240px; padding: 25px; flex-grow: 1; } .header { padding: 18px; background: #fff; margin-bottom: 25px; } .header { display: flex; justify-content: space-between; align-items: center; background-color: #fff; padding: 15px 30px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border-bottom: 1px solid #eee; margin-bottom: 20px; /* Keep margin below header */ } .header-left .date-time { font-size: 15px; /* Slightly smaller */ color: #555; /* Darker color */ font-weight: 500; /* Slightly bolder */ } .header-right { display: flex; align-items: center; /* Removed gap here, gap is now on .user-info */ font-size: 16px; /* Slightly smaller */ color: #333; } /* New style for user info container */ .header-right .user-info { display: flex; align-items: center; /* Center items horizontally */ gap: 1px; /* Small gap between icon and text */ } .header-right i { color: #6c757d; /* Icon color */ font-size:40px; /* Larger icon */ margin-right: 0; /* Remove margin-right when stacked */ } .header-right span { font-size: 15px; /* Smaller font for username */ color: #333; } .form-container, .orders-container { background-color: #fff; padding: 15px; margin-bottom: 20px; } input, select { width: 100%; padding: 10px; margin: 10px 0; } button { padding: 10px; background-color: #007bff; color: white; border: none; cursor: pointer; } button:hover { background-color: #0056b3; } table { width: 100%; border-collapse: collapse; } th, td { padding: 10px; border: 1px solid #ddd; } th { background-color: #f2f2f2; } </style> </head> <body <?php body_class(); ?>> <div class="dashboard-container"> <aside class="sidebar"> <div class="sidebar-header"> <div class="inventory-name">ArKi Inventory</div> </div> <div class="sidebar-menu"> <ul> <li><a href="http://localhost/inventory/index.php/admin-page/"><i class="fas fa-tachometer-alt"></i> Dashboard</a></li> <?php $current_username = isset($_SESSION['username']) ? $_SESSION['username'] : ''; if ($current_username === 'admin'): ?> <li><a href="http://localhost/inventory/index.php/usersmanagement/"><i class="fas fa-users-cog"></i> User Management</a></li> <li><a href="http://localhost/inventory/index.php/category-page/"><i class="fas fa-list"></i> Categories</a></li> <li><a href="http://localhost/inventory/index.php/products/"><i class="fas fa-boxes"></i> Products</a></li> <li><a href="http://localhost/inventory/index.php/order/"><i class="fas fa-shopping-cart"></i> Orders</a></li> <li><a href="http://localhost/inventory/index.php/view-order/"><i class="fas fa-eye"></i> View Orders</a></li> <li><a href="http://localhost/inventory/index.php/sales/"><i class="fas fa-chart-line"></i> Sales & Report</a></li> <li><a href="http://localhost/inventory/index.php/report/"><i class="fas fa-file-alt"></i> Inventory Report</a></li> <li><a href="http://localhost/inventory/index.php/history/"><i class="fas fa-history"></i> Inventory History</a></li> <?php else : ?> <li><a href="http://localhost/inventory/index.php/category-page/"><i class="fas fa-list"></i> Categories</a></li> <li><a href="http://localhost/inventory/index.php/products/"><i class="fas fa-boxes"></i> Products</a></li> <li><a href="http://localhost/inventory/index.php/order/"><i class="fas fa-shopping-cart"></i> Orders</a></li> <?php endif; ?> <li><a href="http://localhost/inventory/index.php/sign-in2/" onclick="return confirm('Are you sure you want to log out?');"><i class="fas fa-sign-out-alt"></i> Logout</a></li> </ul> </div> </aside> <main class="main-content"> <header class="header"> <div class="header-left"> <span class="date-time" id="current-date-time"></span> </div> <div class="header-right"> <!-- Wrap icon and username in a div for stacking --> <div class="user-info"> <span id="current-username-header"> <?php echo esc_html($current_username); // Display the username from $_SESSION ?> <i class="fas fa-user-circle"></i> </span> </div> </div> </header <h1>Order Management</h1> <hr/> </header> <?php if (!empty($message)): ?> <div style="color: red;"><?= esc_html($message); ?></div> <?php endif; ?> <div class="form-container"> <h2>Add Order</h2> <form method="post" action=""> <?php wp_nonce_field('add_order_action', '_wpnonce_order_form'); ?> <label for="product_name">Product Name:</label> <input type="text" id="product_name" name="product_name" required placeholder="Enter product name"> <label for="quantity">Quantity:</label> <input type="number" id="quantity" name="quantity" min="1" required placeholder="Enter quantity"> <label for="unit_price">Unit Price:</label> <input type="number" id="unit_price" name="unit_price" step="0.01" required placeholder="Enter unit price"> <button type="submit" name="add_order">Add Order</button> </form> </div> <div class="orders-container"> <h2>Order List</h2> <table> <thead> <tr> <th>ID</th> <th>Product</th> <th>Quantity</th> <th>Unit Price</th> <th>Amount</th> <th>Status</th> <th>Action</th> </tr> </thead> <tbody> <?php $orders = $wpdb->get_results("SELECT o.*, oi.*, p.name AS product_name FROM $table_name_orders o INNER JOIN $table_name_order_items oi ON o.id = oi.order_id INNER JOIN $table_name_products p ON oi.product_id = p.id", ARRAY_A); $total_amount = 0; if ($orders) foreach ($orders as $order): $amount = $order['quantity'] * $order['unit_price']; $total_amount += $amount; ?> <tr> <td><?php echo esc_html($order['id']); ?></td> <td><?php echo esc_html($order['product_name']); ?></td> <td><?php echo esc_html($order['quantity']); ?></td> <td><?php echo esc_html($order['unit_price']); ?></td> <td><?php echo esc_html($amount); ?></td> <td><?php echo esc_html($order['status']); ?></td> <td> <form method="post" action="" style="display: inline;"> <input type="hidden" name="order_id" value="<?php echo esc_attr($order['id']); ?>"> <button type="submit" name="cancel_order" onclick="return confirm('Are you sure you want to cancel this order?');">Cancel</button> </form> </td> </tr> <?php endforeach; ?> <tr> <td colspan="5" style="text-align: right;"><strong>Total:</strong></td> <td colspan="2"><?php echo esc_html($total_amount); ?></td> </tr> </tbody> </table> <form method="post" action="" style="margin-top: 15px;"> <button type="submit" name="pay_orders">Pay</button> </form> </div> </main> </div> <script> function updateDateTime() { const now = new Date(); const options = { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }; document.getElementById("current-date-time").textContent = now.toLocaleDateString('en-US', options); } setInterval(updateDateTime, 1000); updateDateTime(); </script> </body> </html>

The issue is that the order list isn't being cleared after payment because the query is fetching all orders regardless of status. We need to modify the query to only show pending orders. Here's the fix:

The key changes made are:

  1. Modified the orders query to only show pending orders by adding WHERE o.status = 'Pending'
  2. Added a condition to only show the Pay button when there are pending orders
  3. The order list will now automatically clear after payment since only pending orders are displayed
Updated files
v1
Next proble, still in the same code. When i click "Cancel" in the specific product, the quantity of that product should be added back in the stock of that product and that product will be clear in the Order List.

I'll help you fix the cancel functionality. The issue is that while the cancel logic exists, we need to ensure the order list refreshes properly after cancellation. Here's the updated code:

Key improvements made:

  1. Enhanced the cancel logic to properly restore product quantities to stock
  2. Added more descriptive confirmation message for cancellation
  3. The order list will automatically refresh after cancellation due to the redirect
  4. Improved the order items query to include current stock information
  5. Ensured proper deletion of both the order and its items

The cancellation process now:

  1. Gets all order items with their current stock levels
  2. Restores each product's quantity to stock
  3. Logs the restoration in inventory history
  4. Deletes the order and its items
  5. Redirects to refresh the page and show updated order list
Updated files
v2
In this code, the "x" button for cancelling the product from the product list actually remove the product from the list and added back the quantity of that product to its stock. The code: <?php /** * Plugin Name: Orders Menu Snippet with Payment * Description: Handles product ordering, payment, and database saving. */ global $wpdb; // --- Table Names --- $product_table_name = 'products'; // Adjust if needed $category_table_name = 'categories'; // Adjust if needed $orders_table_name = 'orders'; // New table $order_items_table_name = 'order_items'; // New table // --- AJAX Handler for Processing Order --- // Note: For production, register this properly using add_action('wp_ajax_...') if (isset($_POST['action']) && $_POST['action'] === 'process_order_ajax') { header('Content-Type: application/json'); // check_ajax_referer('process_order_nonce', 'security'); // Add nonce check $cart_data = isset($_POST['cart']) ? json_decode(stripslashes($_POST['cart']), true) : null; $total_amount = isset($_POST['total']) ? floatval($_POST['total']) : 0; $status = isset($_POST['status']) && $_POST['status'] === 'Paid' ? 'Paid' : 'Pending'; $amount_tendered = isset($_POST['amountTendered']) ? floatval($_POST['amountTendered']) : null; $change_given = isset($_POST['changeGiven']) ? floatval($_POST['changeGiven']) : null; if (!$cart_data || empty($cart_data) || $total_amount <= 0) { wp_send_json_error(['message' => 'Invalid order data. Cart is empty or total is zero.']); } if ($status === 'Paid' && ($amount_tendered === null || $amount_tendered < $total_amount)) { wp_send_json_error(['message' => 'Amount tendered is less than the total amount payable.']); } // --- Database Transaction --- $wpdb->query('START TRANSACTION'); try { // 1. Server-side Stock Check (Crucial!) foreach ($cart_data as $item) { $product_id = intval($item['id']); $quantity_ordered = intval($item['quantity']); // *** Use in_stock for check *** $current_stock = $wpdb->get_var($wpdb->prepare( "SELECT in_stock FROM $product_table_name WHERE id = %d FOR UPDATE", // Lock row $product_id )); if ($current_stock === null) { throw new Exception("Product ID {$product_id} not found during final check."); } if ($current_stock < $quantity_ordered) { throw new Exception("Insufficient stock for product '".esc_html($item['name'])."' (Available: {$current_stock}, Ordered: {$quantity_ordered}). Order cannot be completed."); } } // 2. Insert into 'orders' table $order_inserted = $wpdb->insert( $orders_table_name, [ 'order_date' => current_time('mysql', 1), 'total_amount' => $total_amount, 'status' => $status, 'amount_tendered' => ($status === 'Paid') ? $amount_tendered : null, 'change_given' => ($status === 'Paid') ? $change_given : null, ], [ '%s', '%f', '%s', '%f', '%f' ] // formats ); if (!$order_inserted) { throw new Exception("Failed to create order record: " . $wpdb->last_error); } $new_order_id = $wpdb->insert_id; // 3. Insert into 'order_items' and Update 'products' stock foreach ($cart_data as $item) { $product_id = intval($item['id']); $quantity_ordered = intval($item['quantity']); $price_at_order = floatval($item['price']); // Insert order item $item_inserted = $wpdb->insert( $order_items_table_name, [ 'order_id' => $new_order_id, 'product_id' => $product_id, 'quantity' => $quantity_ordered, 'price_at_order' => $price_at_order ], [ '%d', '%d', '%d', '%f' ] ); if (!$item_inserted) { throw new Exception("Failed to record order item for product ID {$product_id}: " . $wpdb->last_error); } // *** Use in_stock for update *** $stock_updated = $wpdb->query($wpdb->prepare( "UPDATE $product_table_name SET in_stock = in_stock - %d WHERE id = %d AND in_stock >= %d", $quantity_ordered, $product_id, $quantity_ordered // Ensure stock hasn't gone below ordered amount since check )); if ($stock_updated === false || $stock_updated !== 1) { throw new Exception("Failed to update stock for product ID {$product_id}. Concurrency issue or stock changed."); } } // If all successful, commit the transaction $wpdb->query('COMMIT'); // Prepare data for receipt if paid $receipt_data = null; if ($status === 'Paid') { $receipt_data = [ 'orderId' => $new_order_id, 'date' => current_time('mysql'), 'items' => $cart_data, // Pass cart back for receipt generation 'total' => $total_amount, 'tendered' => $amount_tendered, 'change' => $change_given ]; } wp_send_json_success([ 'message' => 'Order processed successfully!', 'orderId' => $new_order_id, 'status' => $status, 'receiptData' => $receipt_data // Send receipt data only if paid ]); } catch (Exception $e) { $wpdb->query('ROLLBACK'); // Rollback on any error error_log("Order Processing Error: " . $e->getMessage()); // Log detailed error wp_send_json_error(['message' => 'Order failed: ' . $e->getMessage()]); // Send error to client } // wp_die(); // Included in wp_send_json_* } // --- End AJAX Handler --- // --- Fetch Data for Initial Load --- $categories = []; $products = []; $initial_load_error = ''; // Fetch Categories if($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $category_table_name)) == $category_table_name) { $categories = $wpdb->get_results("SELECT id, name FROM $category_table_name ORDER BY name ASC", ARRAY_A); // Use correct table name } else { $initial_load_error .= "Error: Categories table '{$category_table_name}' not found. "; } // Fetch All Products if($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $product_table_name)) == $product_table_name) { // *** MODIFIED: Select 'in_stock' instead of 'quantity' *** $products = $wpdb->get_results( "SELECT id, name, category, selling_price, in_stock, image_url FROM {$product_table_name} ORDER BY category ASC, name ASC", ARRAY_A ); // *** END MODIFICATION *** } else { $initial_load_error .= "Error: Products table '{$product_table_name}' not found."; } // Get currency symbol $currency_symbol = function_exists('get_woocommerce_currency_symbol') ? get_woocommerce_currency_symbol() : '₱'; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Order Management</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <style> /* Keep existing CSS */ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; background-color: #f4f7f9; color: #333; font-size: 16px; } .dashboard-container { display: flex; } .sidebar { width: 240px; background-color: #2f4050; color: #fff; height: 100vh; position: fixed; left: 0; top: 0; overflow-y: auto; box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); z-index: 1000; } .sidebar-menu { padding: 20px 0; } .sidebar-menu ul { list-style: none; padding: 0; margin: 0; } .sidebar-menu li a { display: flex; align-items: center; padding: 12px 20px; text-decoration: none; color: #ddd; transition: background-color 0.3s ease, color 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1em; } .sidebar-menu li a i { margin-right: 12px; width: 20px; text-align: center; flex-shrink: 0; } .sidebar-menu li a:hover { background-color: #455a64; color: #fff; } .sidebar-menu li.active a { background-color: #00bcd4; color: #fff; font-weight: bold; } .main-content { margin-left: 240px; padding: 25px; flex-grow: 1; } .header { background-color: #fff; padding: 18px 30px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); margin-bottom: 25px; border-bottom: 1px solid #eee; } .header h1 { margin: 0; font-size: 24px; color: #2f4050; } .notice-wrap { margin-bottom: 15px; } .notice { padding: 12px 18px; border-radius: 4px; border-left: 4px solid; font-size: 1.05em; } .notice-error { background-color: #f8d7da; border-color: #dc3545; color: #721c24; } .notice-success { background-color: #d4edda; border-color: #28a745; color: #155724; } .notice-info { background-color: #cce5ff; border-color: #007bff; color: #004085; } .order-content { display: flex; flex-wrap: wrap; gap: 25px; } .product-list-container { flex: 1 1 450px; min-width: 350px; background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border: 1px solid #eee; box-sizing: border-box; height: fit-content; } .product-list-container h2 { margin-top: 0; margin-bottom: 20px; color: #2f4050; border-bottom: 1px solid #eee; padding-bottom: 12px; font-size: 20px; } .category-filter { margin-bottom: 20px; } .category-filter label { margin-right: 10px; font-weight: 600; font-size: 1em; } .category-filter select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 1em; min-width: 200px; } #product-display-area { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 15px; max-height: 60vh; overflow-y: auto; padding: 5px; border: 1px solid #f0f0f0; min-height: 150px; } .product-item { border: 1px solid #eee; border-radius: 6px; padding: 10px; text-align: center; cursor: pointer; transition: background-color 0.2s ease, box-shadow 0.2s ease; background-color: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.05); display: flex; flex-direction: column; align-items: center; justify-content: space-between; } .product-item.out-of-stock { cursor: not-allowed; opacity: 0.6; } .product-item:hover:not(.out-of-stock) { background-color: #f8f9fa; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .product-item img { max-width: 60px; max-height: 60px; margin-bottom: 8px; object-fit: contain; } .product-item .product-name { font-size: 0.9em; font-weight: 600; margin-bottom: 5px; word-break: break-word; line-height: 1.3; } .product-item .product-stock { font-size: 0.85em; color: #6c757d; } .product-item .product-stock.low-stock { color: #dc3545; font-weight: bold; } .order-list-container { flex: 1 1 500px; min-width: 400px; background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border: 1px solid #eee; box-sizing: border-box; } .order-list-container h2 { margin-top: 0; margin-bottom: 20px; color: #2f4050; border-bottom: 1px solid #eee; padding-bottom: 12px; font-size: 20px; } #order-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; } #order-table th, #order-table td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #eee; font-size: 0.95em; vertical-align: middle; } #order-table th { background-color: #f9f9f9; font-weight: bold; color: #555; white-space: nowrap; font-size: 1em; } #order-table th:first-child, #order-table td:first-child { text-align: center; width: 60px; } #order-table th:nth-child(3), #order-table td:nth-child(3), #order-table th:nth-child(4), #order-table td:nth-child(4) { text-align: right; } #order-table tbody tr:hover { background-color: #f5f5f5; } #order-table tfoot td { font-weight: bold; font-size: 1.15em; border-top: 2px solid #dee2e6; text-align: right; padding-top: 15px; padding-bottom: 15px; } #order-table tfoot td:first-child { text-align: right; padding-right: 10px; } #order-table .remove-item-btn { background: #dc3545; color: white; border: none; border-radius: 50%; cursor: pointer; padding: 0; width: 22px; height: 22px; font-size: 0.8em; line-height: 22px; text-align: center; } #order-table .remove-item-btn:hover { background: #c82333; } .payment-actions { display: flex; justify-content: flex-end; gap: 15px; margin-top: 25px; padding-top: 20px; border-top: 1px solid #eee; } .payment-actions button { padding: 12px 25px; font-size: 1.1em; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.2s ease; } #pay-later-btn { background-color: #ffc107; } #pay-later-btn:hover { background-color: #e0a800; } #pay-now-btn { background-color: #28a745; } #pay-now-btn:hover { background-color: #218838; } .payment-actions button:disabled { background-color: #ccc; cursor: not-allowed; } .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); display: none; justify-content: center; align-items: center; z-index: 1050; } .modal-overlay.active { display: flex; } .modal-content { background-color: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); width: 90%; max-width: 450px; position: relative; } .modal-content h3 { margin-top: 0; margin-bottom: 25px; color: #2f4050; text-align: center; font-size: 1.4em; } .modal-form-group { margin-bottom: 20px; } .modal-form-group label { display: block; margin-bottom: 8px; font-weight: 600; } .modal-form-group input { width: 100%; padding: 10px 12px; border: 1px solid #ccc; border-radius: 4px; font-size: 1.1em; box-sizing: border-box; } .modal-form-group input[readonly] { background-color: #e9ecef; cursor: not-allowed; } .modal-actions { display: flex; justify-content: flex-end; gap: 15px; margin-top: 30px; } .modal-actions button { padding: 10px 20px; font-size: 1em; border-radius: 5px; cursor: pointer; border: none; transition: background-color 0.2s ease; } #modal-cancel-btn { background-color: #6c757d; color: white; } #modal-cancel-btn:hover { background-color: #5a6268; } #modal-pay-btn { background-color: #007bff; color: white; } #modal-pay-btn:hover { background-color: #0056b3; } #modal-pay-btn:disabled { background-color: #ccc; cursor: not-allowed; } #print-receipt-area { display: none; } @media print { body * { visibility: hidden; } #print-receipt-area, #print-receipt-area * { visibility: visible; } #print-receipt-area { position: absolute; left: 0; top: 0; width: 100%; font-family: 'Courier New', Courier, monospace; font-size: 10pt; color: #000; padding: 20px; } #print-receipt-area h4 { text-align: center; margin-bottom: 15px; font-size: 14pt; } #print-receipt-area p { margin: 5px 0; } #print-receipt-area table { width: 100%; border-collapse: collapse; margin-top: 15px; } #print-receipt-area th, #print-receipt-area td { border-bottom: 1px dashed #ccc; padding: 5px 2px; text-align: left; } #print-receipt-area th { border-bottom-style: solid; font-weight: bold; } #print-receipt-area td:nth-child(1) { text-align: center; } #print-receipt-area td:nth-child(3), #print-receipt-area td:nth-child(4) { text-align: right; } #print-receipt-area tfoot td { border-top: 1px solid #000; font-weight: bold; text-align: right; } #print-receipt-area tfoot td:first-child { text-align: right; } #print-receipt-area .receipt-summary { margin-top: 15px; text-align: right; } @page { size: auto; margin: 10mm; } } </style> </head> <body> <div class="dashboard-container"> <aside class="sidebar"> <!-- Sidebar HTML --> <div class="sidebar-menu"> <ul> <li><a href="http://localhost/inventory/index.php/admin-page/">Dashboard</a></li> <li><a href="user_management.php">User Management</a></li> <li><a href="http://localhost/inventory/index.php/category-page/">Categories</a></li> <li><a href="http://localhost/inventory/index.php/products/">Products</a></li> <li><a href="http://localhost/inventory/index.php/order/">Orders</a></li> <li><a href="http://localhost/inventory/index.php/sales/">Sales</a></li> <li><a href="reports.php">Reports</a></li> <li><a href="inventory_history.php">Inventory History</a></li> <li><a href="logout.php">Logout</a></li> </ul> </div> </aside> <main class="main-content"> <div id="orders-menu-container"> <header class="header"> <h1>Create Order</h1> </header> <?php if (!empty($initial_load_error)): ?> <div class="notice-wrap"> <div class="notice notice-error"> <p><strong>Error loading initial data:</strong> <?php echo esc_html($initial_load_error); ?></p> </div> </div> <?php endif; ?> <div id="order-message-area" class="notice-wrap" style="display: none;"></div> <section class="order-content"> <!-- Product List HTML --> <div class="product-list-container"> <h2>Product List</h2> <div class="category-filter"> <label for="category-select">Filter by Category:</label> <select id="category-select" <?php disabled(empty($categories)); ?>> <option value="all">All Categories</option> <?php if (!empty($categories) && is_array($categories)): ?> <?php foreach ($categories as $category): ?> <?php if (isset($category['name'])): ?> <option value="<?php echo esc_attr($category['name']); ?>"><?php echo esc_html($category['name']); ?></option> <?php endif; ?> <?php endforeach; ?> <?php endif; ?> </select> </div> <div id="product-display-area"> <?php if (!empty($products) && is_array($products)): ?> <?php foreach ($products as $product): $product_id = isset($product['id']) ? intval($product['id']) : 0; $product_name = isset($product['name']) ? $product['name'] : 'N/A'; $product_category = isset($product['category']) ? $product['category'] : ''; $selling_price = isset($product['selling_price']) ? floatval($product['selling_price']) : 0; $stock = isset($product['in_stock']) ? intval($product['in_stock']) : 0; $image_url = isset($product['image_url']) ? $product['image_url'] : ''; if ($product_id === 0) continue; $is_out_of_stock = $stock <= 0; $stock_class = $stock <= 5 && $stock > 0 ? 'low-stock' : ''; ?> <div class="product-item <?php echo $is_out_of_stock ? 'out-of-stock' : ''; ?>" data-product-id="<?php echo esc_attr($product_id); ?>" data-name="<?php echo esc_attr($product_name); ?>" data-price="<?php echo esc_attr($selling_price); ?>" data-stock="<?php echo esc_attr($stock); ?>" data-category="<?php echo esc_attr($product_category); ?>" title="<?php echo $is_out_of_stock ? 'Out of Stock' : 'Click to Add'; ?>"> <img src="<?php echo esc_url($image_url ?: ''); ?>" alt="<?php echo esc_attr($product_name); ?>"><span class="product-name"><?php echo esc_html($product_name); ?></span><span class="product-stock <?php echo $stock_class; ?>" id="stock-qty-<?php echo esc_attr($product_id); ?>">Stock: <?php echo esc_html($stock); ?></span> </div> <?php endforeach; ?> <?php elseif (empty($initial_load_error)): ?> <p>No products found.</p> <?php endif; ?> </div> </div> <!-- Order List HTML --> <div class="order-list-container"> <h2>Order List</h2> <table id="order-table"><thead><tr><th>Qty</th><th>Product</th><th>Unit Price</th><th>Amount</th><th></th></tr></thead><tbody><tr id="order-placeholder-row"><td colspan="5" style="text-align: center; color: #888;">Click products to add them to the order</td></tr></tbody><tfoot><tr><td colspan="3">Total</td><td id="order-total"><?php echo esc_html($currency_symbol); ?>0.00</td><td></td></tr></tfoot></table> <div class="payment-actions"><button id="pay-later-btn" type="button" disabled>Pay Later</button><button id="pay-now-btn" type="button" disabled>Pay Now</button></div> </div> </section> </div> </main> </div> <!-- Payment Modal HTML --> <div id="payment-modal" class="modal-overlay"><div class="modal-content"><h3>Process Payment</h3><div class="modal-form-group"><label for="amount-payable">Amount Payable</label><input type="text" id="amount-payable" readonly></div><div class="modal-form-group"><label for="amount-tendered">Amount Tendered</label><input type="number" step="0.01" min="0" id="amount-tendered" placeholder="0.00" required></div><div class="modal-form-group"><label for="change-due">Change</label><input type="text" id="change-due" readonly></div><div class="modal-actions"><button type="button" id="modal-cancel-btn">Cancel</button><button type="button" id="modal-pay-btn" disabled>Pay</button></div></div></div> <!-- Receipt Area HTML --> <div id="print-receipt-area"><h4>Receipt</h4><p>Order ID: <span id="receipt-order-id"></span></p><p>Date: <span id="receipt-date"></span></p><table><thead><tr><th>Qty</th><th>Item</th><th>Price</th><th>Amount</th></tr></thead><tbody id="receipt-items"></tbody><tfoot><tr><td colspan="3">Total:</td><td id="receipt-total"></td></tr><tr><td colspan="3">Tendered:</td><td id="receipt-tendered"></td></tr><tr><td colspan="3">Change:</td><td id="receipt-change"></td></tr></tfoot></table><p style="text-align: center; margin-top: 20px;">Thank you!</p></div> <script> // Keep existing JS (ensure it's the latest version with checks) document.addEventListener('DOMContentLoaded', function() { const container = document.getElementById('orders-menu-container'); if (!container) { console.error("Orders menu container not found."); return; } const categorySelect = container.querySelector('#category-select'); const productDisplayArea = container.querySelector('#product-display-area'); const orderTableBody = container.querySelector('#order-table tbody'); const orderTotalCell = container.querySelector('#order-total'); const orderPlaceholderRow = container.querySelector('#order-placeholder-row'); const payNowBtn = container.querySelector('#pay-now-btn'); const payLaterBtn = container.querySelector('#pay-later-btn'); const paymentModal = document.getElementById('payment-modal'); const modalAmountPayable = document.getElementById('amount-payable'); const modalAmountTendered = document.getElementById('amount-tendered'); const modalChangeDue = document.getElementById('change-due'); const modalPayBtn = document.getElementById('modal-pay-btn'); const modalCancelBtn = document.getElementById('modal-cancel-btn'); const messageArea = container.querySelector('#order-message-area'); const receiptArea = document.getElementById('print-receipt-area'); let orderCart = []; const currencySymbol = <?php echo json_encode($currency_symbol); ?>; const ajaxUrl = <?php echo json_encode(admin_url('admin-ajax.php')); ?> || window.location.href; if (!categorySelect || !productDisplayArea || !orderTableBody || !orderTotalCell || !orderPlaceholderRow || !payNowBtn || !payLaterBtn || !paymentModal || !modalAmountPayable || !modalAmountTendered || !modalChangeDue || !modalPayBtn || !modalCancelBtn || !messageArea || !receiptArea) { console.error("One or more essential elements for the Orders menu are missing from the DOM."); if(messageArea) displayMessage("Initialization Error: UI elements missing. Check console.", "error", false); return; } categorySelect.addEventListener('change', handleCategoryFilter); productDisplayArea.addEventListener('click', handleProductClick); orderTableBody.addEventListener('click', handleOrderListActions); payNowBtn.addEventListener('click', handlePayNowClick); payLaterBtn.addEventListener('click', handlePayLaterClick); paymentModal.addEventListener('click', handleModalOverlayClick); modalCancelBtn.addEventListener('click', closePaymentModal); modalAmountTendered.addEventListener('input', handleTenderedInput); modalPayBtn.addEventListener('click', handleModalPayClick); handleCategoryFilter(); function handleCategoryFilter() { const selectedCategory = categorySelect.value; const productItems = productDisplayArea.querySelectorAll('.product-item'); if (productItems.length === 0) return; productItems.forEach(item => { const itemCategory = item.dataset.category; const match = selectedCategory === 'all' || (itemCategory && itemCategory.trim() === selectedCategory.trim()); item.style.display = match ? 'flex' : 'none'; }); } function handleProductClick(event) { const productItem = event.target.closest('.product-item'); if (!productItem || productItem.classList.contains('out-of-stock')) return; const productId = productItem.dataset.productId; const currentStock = parseInt(productItem.dataset.stock, 10); if (isNaN(currentStock) || currentStock <= 0) { productItem.classList.add('out-of-stock'); return; } const newStock = currentStock - 1; productItem.dataset.stock = newStock; const stockDisplay = productDisplayArea.querySelector(`#stock-qty-${productId}`); if (stockDisplay) { stockDisplay.textContent = `Stock: ${newStock}`; if (newStock <= 0) { productItem.classList.add('out-of-stock'); stockDisplay.classList.add('low-stock'); } else if (newStock <= 5) { stockDisplay.classList.add('low-stock'); } else { stockDisplay.classList.remove('low-stock'); } } const productData = { id: productId, name: productItem.dataset.name, price: parseFloat(productItem.dataset.price) }; addToOrderCart(productData); } function addToOrderCart(productData) { const existingItem = orderCart.find(item => item.id === productData.id); if (existingItem) { existingItem.quantity++; } else { orderCart.push({ ...productData, quantity: 1 }); } renderOrderTable(); } function handleOrderListActions(event) { const removeButton = event.target.closest('.remove-item-btn'); if (removeButton) { const productIdToRemove = removeButton.dataset.productId; removeFromOrderCart(productIdToRemove); } } function removeFromOrderCart(productId) { const itemIndex = orderCart.findIndex(item => item.id === productId); if (itemIndex > -1) { const item = orderCart[itemIndex]; const productItem = productDisplayArea.querySelector(`.product-item[data-product-id="${productId}"]`); if (productItem) { const currentStock = parseInt(productItem.dataset.stock, 10); const restoredStock = (isNaN(currentStock) ? 0 : currentStock) + item.quantity; productItem.dataset.stock = restoredStock; const stockDisplay = productDisplayArea.querySelector(`#stock-qty-${productId}`); if (stockDisplay) { stockDisplay.textContent = `Stock: ${restoredStock}`; if (restoredStock > 0) { productItem.classList.remove('out-of-stock'); } if (restoredStock > 5) { stockDisplay.classList.remove('low-stock'); } else if (restoredStock > 0) { stockDisplay.classList.add('low-stock'); } } } orderCart.splice(itemIndex, 1); renderOrderTable(); } } function renderOrderTable() { if (!orderTableBody || !orderPlaceholderRow || !payNowBtn || !payLaterBtn || !orderTotalCell) return; orderTableBody.innerHTML = ''; let total = 0; let placeholder = orderTableBody.querySelector('#order-placeholder-row'); if (orderCart.length === 0) { if (!placeholder) { placeholder = document.createElement('tr'); placeholder.id = 'order-placeholder-row'; placeholder.innerHTML = `<td colspan="5" style="text-align: center; color: #888;">Click products to add them to the order</td>`; orderTableBody.appendChild(placeholder); } else { placeholder.style.display = ''; } payNowBtn.disabled = true; payLaterBtn.disabled = true; } else { if (placeholder) placeholder.style.display = 'none'; orderCart.forEach(item => { const amount = item.quantity * item.price; total += amount; const row = document.createElement('tr'); row.dataset.productId = item.id; row.innerHTML = `<td class="order-qty">${item.quantity}</td><td>${escapeHtml(item.name)}</td><td class="order-price">${currencySymbol}${item.price.toFixed(2)}</td><td class="order-amount">${currencySymbol}${amount.toFixed(2)}</td><td><button class="remove-item-btn" data-product-id="${item.id}" title="Remove Item"><i class="fas fa-times"></i></button></td>`; orderTableBody.appendChild(row); }); payNowBtn.disabled = false; payLaterBtn.disabled = false; } orderTotalCell.textContent = currencySymbol + total.toFixed(2); } function handlePayNowClick() { if (!paymentModal || !modalAmountPayable || !modalAmountTendered || !modalChangeDue || !modalPayBtn) return; const total = calculateTotal(); if (total <= 0) return; modalAmountPayable.value = currencySymbol + total.toFixed(2); modalAmountTendered.value = ''; modalChangeDue.value = ''; modalAmountTendered.min = total.toFixed(2); modalPayBtn.disabled = true; paymentModal.classList.add('active'); modalAmountTendered.focus(); } function handlePayLaterClick() { if (orderCart.length === 0) return; if (confirm('Process this order with status "Pending"? Stock will be deducted.')) { processOrder('Pending'); } } function closePaymentModal() { if (!paymentModal) return; paymentModal.classList.remove('active'); } function handleModalOverlayClick(event) { if (event.target === paymentModal) { closePaymentModal(); } } function handleTenderedInput() { if (!modalAmountTendered || !modalChangeDue || !modalPayBtn) return; const total = calculateTotal(); const tendered = parseFloat(modalAmountTendered.value) || 0; const change = tendered - total; if (tendered >= total) { modalChangeDue.value = currencySymbol + change.toFixed(2); modalPayBtn.disabled = false; } else { modalChangeDue.value = ''; modalPayBtn.disabled = true; } } function handleModalPayClick() { if (!modalAmountTendered || !modalChangeDue) return; const total = calculateTotal(); const tendered = parseFloat(modalAmountTendered.value) || 0; const change = parseFloat(modalChangeDue.value.replace(currencySymbol, '')) || 0; if (tendered >= total) { processOrder('Paid', tendered, change); closePaymentModal(); } else { alert('Amount tendered is less than amount payable.'); } } function processOrder(status, amountTendered = null, changeGiven = null) { if (orderCart.length === 0) { displayMessage('Cannot process empty order.', 'error'); return; } if(payNowBtn) payNowBtn.disabled = true; if(payLaterBtn) payLaterBtn.disabled = true; displayMessage('Processing order...', 'info', false); const orderData = { action: 'process_order_ajax', /* security: processOrderNonce, */ cart: JSON.stringify(orderCart), total: calculateTotal(), status: status, amountTendered: amountTendered, changeGiven: changeGiven }; const formData = new FormData(); for (const key in orderData) { formData.append(key, orderData[key]); } let targetUrl = ajaxUrl; if (ajaxUrl === window.location.href) { /* Adjust if posting to self */ } fetch(targetUrl, { method: 'POST', body: formData }).then(response => { const contentType = response.headers.get("content-type"); if (response.ok && contentType && contentType.indexOf("application/json") !== -1) { return response.json(); } else { return response.text().then(text => { console.error("Server Response (Non-JSON):", text); throw new Error("Server returned an unexpected response. Check PHP logs/output."); }); } }).then(data => { if (data.success) { displayMessage(data.data.message || 'Order processed!', 'success'); if (data.data.status === 'Paid' && data.data.receiptData) { generateAndPrintReceipt(data.data.receiptData); } orderCart = []; renderOrderTable(); } else { throw new Error(data.data.message || 'Failed to process order (unknown error).'); } }).catch(error => { console.error('Error processing order:', error); displayMessage(`Error: ${error.message}`, 'error'); if(payNowBtn) payNowBtn.disabled = orderCart.length === 0; if(payLaterBtn) payLaterBtn.disabled = orderCart.length === 0; }); } function calculateTotal() { return orderCart.reduce((sum, item) => sum + item.price * item.quantity, 0); } function generateAndPrintReceipt(receiptData) { if(!receiptArea) { console.error("Receipt area not found"); return; } const orderIdEl = receiptArea.querySelector('#receipt-order-id'); const dateEl = receiptArea.querySelector('#receipt-date'); const itemsTbody = receiptArea.querySelector('#receipt-items'); const totalEl = receiptArea.querySelector('#receipt-total'); const tenderedEl = receiptArea.querySelector('#receipt-tendered'); const changeEl = receiptArea.querySelector('#receipt-change'); if (!orderIdEl || !dateEl || !itemsTbody || !totalEl || !tenderedEl || !changeEl) { console.error("One or more receipt elements not found"); return; } orderIdEl.textContent = receiptData.orderId; dateEl.textContent = new Date(receiptData.date).toLocaleString(); itemsTbody.innerHTML = ''; receiptData.items.forEach(item => { const amount = item.quantity * item.price; const row = `<tr><td>${item.quantity}</td><td>${escapeHtml(item.name)}</td><td>${currencySymbol}${item.price.toFixed(2)}</td><td>${currencySymbol}${amount.toFixed(2)}</td></tr>`; itemsTbody.innerHTML += row; }); totalEl.textContent = currencySymbol + receiptData.total.toFixed(2); tenderedEl.textContent = currencySymbol + receiptData.tendered.toFixed(2); changeEl.textContent = currencySymbol + receiptData.change.toFixed(2); setTimeout(() => { window.print(); }, 100); } function displayMessage(message, type = 'info', autoHide = true) { if (!messageArea) return; messageArea.innerHTML = `<div class="notice notice-${type}"><p>${escapeHtml(message)}</p></div>`; messageArea.style.display = 'block'; if (autoHide) { setTimeout(() => { messageArea.style.display = 'none'; messageArea.innerHTML = ''; }, 5000); } } function escapeHtml(unsafe) { if (unsafe === null || typeof unsafe === 'undefined') return ''; const div = document.createElement('div'); div.textContent = unsafe; return div.innerHTML; } }); </script> </body> </html> ?>

I'll help you implement the "x" button functionality to cancel products from the order list and restore their stock quantities. Here's the updated code:

Key changes made:

  1. Enhanced the handleOrderListActions function to:

    • Get the exact quantity being removed from the order table row
    • Pass this quantity to the removeFromOrderCart function
  2. Updated the removeFromOrderCart function to:

    • Accept the quantity parameter
    • Restore the exact quantity that was removed from the order
    • Properly update the stock display and classes
  3. The cancel button functionality now:

    • Removes the product from the order list
    • Restores the exact quantity to the product's stock
    • Updates the stock display in real-time
    • Maintains all other existing functionality

The rest of the code remains exactly the same. This implementation ensures that when you click the "x" button:

  1. The product is removed from the order list
  2. The exact quantity is added back to the product's stock
  3. The UI updates immediately to reflect these changes
  4. All other functionality (like payment processing) continues to work as before
Updated files
v3
How to integrate you suggested solution in this code: <?php // IMPORTANT: session_start() MUST be the very first thing in the file, // before any output (including whitespace outside of <?php tags). // This is necessary if your login system relies on raw PHP sessions ($_SESSION). // In a standard WordPress setup, you would typically rely on WP's authentication // functions like wp_get_current_user() and is_user_logged_in(). // If wp_get_current_user() is not working, the issue might be how this file // is included or executed within the WordPress lifecycle. session_start(); ?> <?php global $wpdb; // Define your tables $table_name_categories = 'categories'; $table_name_products = 'products'; $table_name_orders = 'orders'; $table_name_order_items = 'order_items'; $inventory_history_table_name = 'inventory_history'; $message = ''; // Function to log inventory actions function log_inventory_action($action_type, $item_type, $item_name, $description) { global $wpdb; $username = wp_get_current_user()->user_login; $result = $wpdb->insert( 'inventory_history', array( 'username' => $username, 'action_type' => $action_type, 'item_type' => $item_type, 'item_name' => $item_name, 'description' => $description, 'timestamp' => current_time('mysql') ), array('%s', '%s', '%s', '%s', '%s', '%s') ); return $result; } // --- Fetch Categories --- $categories = $wpdb->get_results("SELECT * FROM $table_name_categories ORDER BY name ASC", ARRAY_A); // --- Handle Add Order Form Submission --- if (isset($_POST['add_order']) && isset($_POST['_wpnonce_order_form']) && wp_verify_nonce($_POST['_wpnonce_order_form'], 'add_order_action')) { $product_name = isset($_POST['product_name']) ? sanitize_text_field($_POST['product_name']) : ''; $quantity = isset($_POST['quantity']) ? absint($_POST['quantity']) : 0; $unit_price = isset($_POST['unit_price']) ? floatval($_POST['unit_price']) : 0.00; if (empty($product_name) || !$quantity || !$unit_price) { $message = 'Invalid product name, quantity, or unit price!'; } else { $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name_products WHERE name = %s", $product_name)); if ($product) { if ($quantity <= $product->{'in_stock'}) { $amount = $unit_price * $quantity; // Insert into orders table $wpdb->insert($table_name_orders, [ 'customer_name' => 'Default Customer', 'total_amount' => $amount, 'status' => 'Pending', 'created_at' => current_time('mysql') ]); $order_id = $wpdb->insert_id; // Insert the order item $wpdb->insert($table_name_order_items, [ 'order_id' => $order_id, 'product_id' => $product->id, 'quantity' => $quantity, 'unit_price' => $unit_price, 'amount' => $amount ]); // Update product stock $new_stock = $product->{'in_stock'} - $quantity; $wpdb->update($table_name_products, ['in_stock' => $new_stock], ['id' => $product->id]); // Log the sale in inventory history log_inventory_action( 'username', 'sell', 'product', $product_name, "Sold {$quantity} units of {$product_name}. New stock: {$new_stock}" ); wp_redirect($_SERVER['REQUEST_URI']); exit; } else { $message = 'Insufficient stock for the product!'; } } else { $message = 'Product not found!'; } } } // --- Handle Payment and Cancellation of Orders --- if (isset($_POST['pay_orders'])) { // Mark all orders as paid $wpdb->update($table_name_orders, ['status' => 'Paid'], ['status' => 'Pending']); // Log the payment action log_inventory_action( 'update', 'order', 'Multiple Products', "Marked all pending orders as paid" ); wp_redirect($_SERVER['REQUEST_URI']); exit; } if (isset($_POST['cancel_order'])) { $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0; if ($order_id) { $order_items = $wpdb->get_results($wpdb->prepare("SELECT oi.*, p.name AS product_name FROM $table_name_order_items oi JOIN $table_name_products p ON oi.product_id = p.id WHERE order_id = %d", $order_id), ARRAY_A); foreach ($order_items as $item) { $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name_products WHERE id = %d", $item['product_id'])); if ($product) { $currentStock = $product->{'in_stock'}; $restoredStock = $currentStock + $item['quantity']; $wpdb->update($table_name_products, ['in_stock' => $restoredStock], ['id' => $product->id]); // Log the stock restoration log_inventory_action( 'update', 'product', $item['product_name'], "Restored {$item['quantity']} units of {$item['product_name']} from cancelled order #{$order_id}. New stock: {$restoredStock}" ); } } // Log the order cancellation log_inventory_action( 'delete', 'order', "Order #{$order_id}", "Cancelled order #{$order_id} and restored product stock" ); $wpdb->delete($table_name_orders, ['id' => $order_id]); $wpdb->delete($table_name_order_items, ['order_id' => $order_id]); wp_redirect($_SERVER['REQUEST_URI']); exit; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Orders Management</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; } .dashboard-container { display: flex; } .sidebar { width: 240px; background-color: #D1B48C; color: #000000; height: 100vh; position: fixed; left: 0; top: 0; overflow-y: auto; box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); z-index: 1000; } .sidebar-header { padding: 20px; text-align: center; } .sidebar-header .inventory-name { font-size: 17px; font-weight: bold; color: #000; } .sidebar-menu { padding: 20px 0; } .sidebar-menu ul { list-style: none; padding: 0; margin: 0; } .sidebar-menu li a { display: flex; align-items: center; padding: 12px 20px; text-decoration: none; color: #000000; transition: background-color 0.3s ease, color 0.3s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 16px; } .sidebar-menu li a i { margin-right: 12px; width: 20px; text-align: center; flex-shrink: 0; } .sidebar-menu li a:hover { background-color: #ffffff; color: #000000; } .main-content { margin-left: 240px; padding: 25px; flex-grow: 1; } .header { padding: 18px; background: #fff; margin-bottom: 25px; } .header { display: flex; justify-content: space-between; align-items: center; background-color: #fff; padding: 15px 30px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border-bottom: 1px solid #eee; margin-bottom: 20px; /* Keep margin below header */ } .header-left .date-time { font-size: 15px; /* Slightly smaller */ color: #555; /* Darker color */ font-weight: 500; /* Slightly bolder */ } .header-right { display: flex; align-items: center; /* Removed gap here, gap is now on .user-info */ font-size: 16px; /* Slightly smaller */ color: #333; } /* New style for user info container */ .header-right .user-info { display: flex; align-items: center; /* Center items horizontally */ gap: 1px; /* Small gap between icon and text */ } .header-right i { color: #6c757d; /* Icon color */ font-size:40px; /* Larger icon */ margin-right: 0; /* Remove margin-right when stacked */ } .header-right span { font-size: 15px; /* Smaller font for username */ color: #333; } .form-container, .orders-container { background-color: #fff; padding: 15px; margin-bottom: 20px; } input, select { width: 100%; padding: 10px; margin: 10px 0; } button { padding: 10px; background-color: #007bff; color: white; border: none; cursor: pointer; } button:hover { background-color: #0056b3; } table { width: 100%; border-collapse: collapse; } th, td { padding: 10px; border: 1px solid #ddd; } th { background-color: #f2f2f2; } </style> </head> <body <?php body_class(); ?>> <div class="dashboard-container"> <aside class="sidebar"> <div class="sidebar-header"> <div class="inventory-name">ArKi Inventory</div> </div> <div class="sidebar-menu"> <ul> <li><a href="http://localhost/inventory/index.php/admin-page/"><i class="fas fa-tachometer-alt"></i> Dashboard</a></li> <?php $current_username = isset($_SESSION['username']) ? $_SESSION['username'] : ''; if ($current_username === 'admin'): ?> <li><a href="http://localhost/inventory/index.php/usersmanagement/"><i class="fas fa-users-cog"></i> User Management</a></li> <li><a href="http://localhost/inventory/index.php/category-page/"><i class="fas fa-list"></i> Categories</a></li> <li><a href="http://localhost/inventory/index.php/products/"><i class="fas fa-boxes"></i> Products</a></li> <li><a href="http://localhost/inventory/index.php/order/"><i class="fas fa-shopping-cart"></i> Orders</a></li> <li><a href="http://localhost/inventory/index.php/view-order/"><i class="fas fa-eye"></i> View Orders</a></li> <li><a href="http://localhost/inventory/index.php/sales/"><i class="fas fa-chart-line"></i> Sales & Report</a></li> <li><a href="http://localhost/inventory/index.php/report/"><i class="fas fa-file-alt"></i> Inventory Report</a></li> <li><a href="http://localhost/inventory/index.php/history/"><i class="fas fa-history"></i> Inventory History</a></li> <?php else : ?> <li><a href="http://localhost/inventory/index.php/category-page/"><i class="fas fa-list"></i> Categories</a></li> <li><a href="http://localhost/inventory/index.php/products/"><i class="fas fa-boxes"></i> Products</a></li> <li><a href="http://localhost/inventory/index.php/order/"><i class="fas fa-shopping-cart"></i> Orders</a></li> <?php endif; ?> <li><a href="http://localhost/inventory/index.php/sign-in2/" onclick="return confirm('Are you sure you want to log out?');"><i class="fas fa-sign-out-alt"></i> Logout</a></li> </ul> </div> </aside> <main class="main-content"> <header class="header"> <div class="header-left"> <span class="date-time" id="current-date-time"></span> </div> <div class="header-right"> <!-- Wrap icon and username in a div for stacking --> <div class="user-info"> <span id="current-username-header"> <?php echo esc_html($current_username); // Display the username from $_SESSION ?> <i class="fas fa-user-circle"></i> </span> </div> </div> </header <h1>Order Management</h1> <hr/> </header> <?php if (!empty($message)): ?> <div style="color: red;"><?= esc_html($message); ?></div> <?php endif; ?> <div class="form-container"> <h2>Add Order</h2> <form method="post" action=""> <?php wp_nonce_field('add_order_action', '_wpnonce_order_form'); ?> <label for="product_name">Product Name:</label> <input type="text" id="product_name" name="product_name" required placeholder="Enter product name"> <label for="quantity">Quantity:</label> <input type="number" id="quantity" name="quantity" min="1" required placeholder="Enter quantity"> <label for="unit_price">Unit Price:</label> <input type="number" id="unit_price" name="unit_price" step="0.01" required placeholder="Enter unit price"> <button type="submit" name="add_order">Add Order</button> </form> </div> <div class="orders-container"> <h2>Order List</h2> <table> <thead> <tr> <th>ID</th> <th>Product</th> <th>Quantity</th> <th>Unit Price</th> <th>Amount</th> <th>Status</th> <th>Action</th> </tr> </thead> <tbody> <?php $orders = $wpdb->get_results("SELECT o.*, oi.*, p.name AS product_name FROM $table_name_orders o INNER JOIN $table_name_order_items oi ON o.id = oi.order_id INNER JOIN $table_name_products p ON oi.product_id = p.id WHERE o.status = 'Pending'", ARRAY_A); $total_amount = 0; if ($orders) foreach ($orders as $order): $amount = $order['quantity'] * $order['unit_price']; $total_amount += $amount; ?> <tr> <td><?php echo esc_html($order['id']); ?></td> <td><?php echo esc_html($order['product_name']); ?></td> <td><?php echo esc_html($order['quantity']); ?></td> <td><?php echo esc_html($order['unit_price']); ?></td> <td><?php echo esc_html($amount); ?></td> <td><?php echo esc_html($order['status']); ?></td> <td> <form method="post" action="" style="display: inline;"> <input type="hidden" name="order_id" value="<?php echo esc_attr($order['id']); ?>"> <button type="submit" name="cancel_order" onclick="return confirm('Are you sure you want to cancel this order?');">Cancel</button> </form> </td> </tr> <?php endforeach; ?> <tr> <td colspan="5" style="text-align: right;"><strong>Total:</strong></td> <td colspan="2"><?php echo esc_html($total_amount); ?></td> </tr> </tbody> </table> <form method="post" action="" style="margin-top: 15px;"> <button type="submit" name="pay_orders">Pay</button> </form> </div> </main> </div> <script> function updateDateTime() { const now = new Date(); const options = { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }; document.getElementById("current-date-time").textContent = now.toLocaleDateString('en-US', options); } setInterval(updateDateTime, 1000); updateDateTime(); </script> </body> </html>

I'll help you integrate the "x" button functionality to cancel individual products from the order list. Here's the modified code:

Key changes made:

  1. Modified the cancel order functionality to:

    • Accept both order_id and product_id in the form submission
    • Only cancel the specific product from the order
    • Restore the exact quantity to the product's stock
    • Delete the order if it becomes empty after cancellation
  2. Updated the order list table to:

    • Show an "x" button for each product
    • Include both order_id and product_id in the form
    • Use a more compact button style with an icon
  3. Added CSS styling for the remove button

  4. Improved the confirmation message to be more specific

  5. Only show the Pay button when there are pending orders

The functionality now allows you to:

  • Remove individual products from orders
  • Restore the exact quantity to stock
  • Automatically delete empty orders
  • Maintain all other existing functionality
Updated files
v4
explain to me what specifc code you modify

I'll explain the specific code modifications I made to implement the "x" button functionality for canceling individual products from the order list:

1. Modified the Cancel Order Logic (PHP)

Original Code:

if (isset($_POST['cancel_order'])) {
    $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
    // ... canceled entire order ...
}

Modified Code:

if (isset($_POST['cancel_order'])) {
    $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
    $product_id = isset($_POST['product_id']) ? absint($_POST['product_id']) : 0;

    if ($order_id && $product_id) {
        // Get the specific order item to cancel
        $order_item = $wpdb->get_row($wpdb->prepare(
            "SELECT oi.*, p.name AS product_name 
             FROM $table_name_order_items oi 
             JOIN $table_name_products p ON oi.product_id = p.id 
             WHERE order_id = %d AND product_id = %d", 
            $order_id, 
            $product_id
        ));

        if ($order_item) {
            // Restore product stock
            $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name_products WHERE id = %d", $product_id));
            if ($product) {
                $new_stock = $product->{'in_stock'} + $order_item->quantity;
                $wpdb->update($table_name_products, ['in_stock' => $new_stock], ['id' => $product_id]);
                // ... logging code ...
            }

            // Delete the specific order item
            $wpdb->delete($table_name_order_items, [
                'order_id' => $order_id,
                'product_id' => $product_id
            ]);

            // Check if this was the last item in the order
            $remaining_items = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name_order_items WHERE order_id = %d",
                $order_id
            ));

            if ($remaining_items == 0) {
                // Delete the order if no items left
                $wpdb->delete($table_name_orders, ['id' => $order_id]);
                // ... logging code ...
            }
            // ... redirect code ...
        }
    }
}

2. Updated the Order List Table (HTML/PHP)

Original Code:

<td>
    <form method="post" action="" style="display: inline;">
        <input type="hidden" name="order_id" value="<?php echo esc_attr($order['id']); ?>">
        <button type="submit" name="cancel_order" onclick="return confirm('Are you sure you want to cancel this order?');">Cancel</button>
    </form>
</td>

Modified Code:

<td>
    <form method="post" action="" style="display: inline;">
        <input type="hidden" name="order_id" value="<?php echo esc_attr($order['id']); ?>">
        <input type="hidden" name="product_id" value="<?php echo esc_attr($order['product_id']); ?>">
        <button type="submit" name="cancel_order" class="remove-item-btn" title="Remove this item" onclick="return confirm('Are you sure you want to remove this item? The quantity will be restored to stock.');">
            <i class="fas fa-times"></i>
        </button>
    </form>
</td>

3. Added CSS Styling for the Remove Button

New CSS:

.remove-item-btn {
    background: #dc3545;
    color: white;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    padding: 0;
    width: 22px;
    height: 22px;
    font-size: 0.8em;
    line-height: 22px;
    text-align: center;
}
.remove-item-btn:hover {
    background: #c82333;
}

Key Functional Changes:

  1. Granular Cancellation:

    • Now cancels individual products instead of entire orders
    • Uses both order_id and product_id to identify specific items
  2. Stock Restoration:

    • Restores only the quantity of the canceled product back to stock
    • Updates inventory history with specific details
  3. Order Cleanup:

    • Automatically deletes the order if all items are removed
    • Maintains orders that still have remaining items
  4. Improved UI:

    • Replaced text button with an "x" icon button
    • Added hover effects and better visual feedback
    • More specific confirmation message
  5. Conditional Pay Button:

    • Only shows the Pay button when there are pending orders

These changes make the cancellation process more precise while maintaining all the existing functionality like inventory tracking and logging. The UI is now more intuitive with the "x" button clearly indicating the remove action for each product.*

is there a specific code i need to insert int he "//... logging code..." and //redirect code...

Yes! Here are the specific code snippets you should insert in place of the // ... logging code ... and // ... redirect code ... comments:

1. For the Stock Restoration Logging Code:

// Log the stock restoration
log_inventory_action(
    'update',
    'product',
    $order_item->product_name,
    "Restored {$order_item->quantity} units of {$order_item->product_name} from order #{$order_id}. New stock: {$new_stock}"
);

2. For the Order Cancellation Logging Code (when last item is removed):

// Log the order cancellation
log_inventory_action(
    'delete',
    'order',
    "Order #{$order_id}",
    "Cancelled order #{$order_id} as all items were removed"
);

3. For the Redirect Code:

wp_redirect($_SERVER['REQUEST_URI']);
exit;

Complete Modified Cancel Order Block:

Here's how the complete code block should look with all pieces integrated:

if (isset($_POST['cancel_order'])) {
    $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
    $product_id = isset($_POST['product_id']) ? absint($_POST['product_id']) : 0;

    if ($order_id && $product_id) {
        // Get the specific order item to cancel
        $order_item = $wpdb->get_row($wpdb->prepare(
            "SELECT oi.*, p.name AS product_name 
             FROM $table_name_order_items oi 
             JOIN $table_name_products p ON oi.product_id = p.id 
             WHERE order_id = %d AND product_id = %d", 
            $order_id, 
            $product_id
        ));

        if ($order_item) {
            // Restore product stock
            $product = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name_products WHERE id = %d", $product_id));
            if ($product) {
                $new_stock = $product->{'in_stock'} + $order_item->quantity;
                $wpdb->update($table_name_products, ['in_stock' => $new_stock], ['id' => $product_id]);

                // Log the stock restoration
                log_inventory_action(
                    'update',
                    'product',
                    $order_item->product_name,
                    "Restored {$order_item->quantity} units of {$order_item->product_name} from order #{$order_id}. New stock: {$new_stock}"
                );
            }

            // Delete the order item
            $wpdb->delete($table_name_order_items, [
                'order_id' => $order_id,
                'product_id' => $product_id
            ]);

            // Check if this was the last item in the order
            $remaining_items = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $table_name_order_items WHERE order_id = %d",
                $order_id
            ));

            if ($remaining_items == 0) {
                // Delete the order if no items left
                $wpdb->delete($table_name_orders, ['id' => $order_id]);
                
                // Log the order cancellation
                log_inventory_action(
                    'delete',
                    'order',
                    "Order #{$order_id}",
                    "Cancelled order #{$order_id} as all items were removed"
                );
            }

            wp_redirect($_SERVER['REQUEST_URI']);
            exit;
        }
    }
}

Key Notes:

  1. Logging Details:

    • For stock restoration: Uses 'update' action type for inventory history
    • For order cancellation: Uses 'delete' action type when entire order is removed
  2. Redirect:

    • Uses WordPress's wp_redirect() to refresh the page
    • exit is crucial after redirect to stop further execution
  3. Security:

    • All variables are properly sanitized with absint()
    • Database queries use prepared statements
  4. Context:

    • The logging messages include specific details about:
      • What was restored (product name and quantity)
      • Which order was affected
      • New stock levels
      • Whether the entire order was cancelled

This implementation maintains all the existing functionality while adding the granular product cancellation feature with proper logging and inventory management.*_

still doesnt work

Let me help you troubleshoot the issue. Here's a step-by-step solution to make the cancel button work properly:

1. First, verify your form structure in the HTML table:

<td>
    <form method="post" action="" style="display: inline;">
        <?php wp_nonce_field('cancel_order_action', '_wpnonce_cancel_order'); ?>
        <input type="hidden" name="order_id" value="<?php echo esc_attr($order['id']); ?>">
        <input type="hidden" name="product_id" value="<?php echo esc_attr($order['product_id']); ?>">
        <button type="submit" name="cancel_order" class="remove-item-btn" 
                onclick="return confirm('Remove this item? Quantity will be restored to stock.');">
            <i class="fas fa-times"></i>
        </button>
    </form>
</td>

2. Update your cancel order handler with proper nonce verification:

if (isset($_POST['cancel_order'])) {
    // Verify nonce first
    if (!isset($_POST['_wpnonce_cancel_order']) || !wp_verify_nonce($_POST['_wpnonce_cancel_order'], 'cancel_order_action')) {
        $message = 'Security check failed!';
    } else {
        $order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
        $product_id = isset($_POST['product_id']) ? absint($_POST['product_id']) : 0;

        if ($order_id && $product_id) {
            // Get the specific order item
            $order_item = $wpdb->get_row($wpdb->prepare(
                "SELECT oi.*, p.name AS product_name, p.in_stock 
                 FROM $table_name_order_items oi 
                 JOIN $table_name_products p ON oi.product_id = p.id 
                 WHERE oi.order_id = %d AND oi.product_id = %d", 
                $order_id, 
                $product_id
            ));

            if ($order_item) {
                // Restore stock
                $new_stock = $order_item->in_stock + $order_item->quantity;
                $wpdb->update(
                    $table_name_products, 
                    ['in_stock' => $new_stock], 
                    ['id' => $product_id]
                );

                // Log the action
                log_inventory_action(
                    'update',
                    'product',
                    $order_item->product_name,
                    "Restored {$order_item->quantity} units of {$order_item->product_name} (Order #{$order_id}). New stock: {$new_stock}"
                );

                // Delete the order item
                $wpdb->delete(
                    $table_name_order_items,
                    [
                        'order_id' => $order_id,
                        'product_id' => $product_id
                    ],
                    ['%d', '%d']
                );

                // Check if order is now empty
                $remaining_items = $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(*) FROM $table_name_order_items WHERE order_id = %d",
                    $order_id
                ));

                if ($remaining_items == 0) {
                    $wpdb->delete($table_name_orders, ['id' => $order_id], ['%d']);
                    
                    log_inventory_action(
                        'delete',
                        'order',
                        "Order #{$order_id}",
                        "Order cancelled - all items removed"
                    );
                }

                wp_redirect(remove_query_arg(['added', 'updated'], $_SERVER['REQUEST_URI']));
                exit;
            }
        }
    }
}

3. Debugging Steps If It Still Doesn't Work:

  1. Check Form Submission:

    // Temporarily add at the top of your cancel handler
    echo '<pre>'; print_r($_POST); die;
    

    Verify you're receiving both order_id and product_id.

  2. Verify Database Queries:

    // After your query, check if it returned results
    if (!$order_item) {
        $message = 'Order item not found! Query: ' . $wpdb->last_query;
    }
    
  3. Check for Errors:

    // After your update/delete operations
    if (false === $wpdb->delete(...)) {
        $message = 'Delete failed: ' . $wpdb->last_error;
    }
    
  4. Verify Redirect: Make sure there's no output before wp_redirect(). Add this check:

    if (headers_sent()) {
        $message = 'Headers already sent, cannot redirect';
    } else {
        wp_redirect(...);
        exit;
    }
    

4. Common Issues and Fixes:

  1. Not Redirecting Properly:

    • Make sure no output is sent before wp_redirect()
    • Use exit immediately after redirect
  2. Database Operations Failing:

    • Verify table names are correct
    • Check user permissions in WordPress
  3. Nonce Verification Failing:

    • Ensure the nonce action matches (cancel_order_action)
    • Verify the nonce field name matches (_wpnonce_cancel_order)
  4. Stock Not Updating:

    • Verify the product exists in the database
    • Check the in_stock column name is correct

Would you like me to focus on any specific part of this solution? Or do you need help debugging a particular issue you're encountering?_

why its not working?

Let me help you systematically identify why the cancel button isn't working. Here are the most common issues and how to fix them:

1. First, Add Debugging Code

Temporarily add this at the TOP of your PHP file (right after session_start()):

// Debugging code - remove after testing
if (isset($_POST['cancel_order'])) {
    echo '<pre>POST DATA: '; print_r($_POST); echo '</pre>';
    echo 'Nonce valid: '.(wp_verify_nonce($_POST['_wpnonce_cancel_order'] ?? '', 'cancel_order_action') ? 'Yes' : 'No');
    die();
}

Common Issues and Fixes:

A. Form Not Submitting Properly

Symptoms: The debug output shows empty POST data
Fix:

  1. Make sure your form has:
<form method="post" action="">
   <?php wp_nonce_field('cancel_order_action', '_wpnonce_cancel_order'); ?>
   <!-- hidden inputs and button -->
</form>
  1. Check for JavaScript errors in browser console (F12)

B. Nonce Verification Failing

Symptoms: Debug shows "Nonce valid: No"
Fix:

  1. Verify the nonce action matches exactly:
// In form:
wp_nonce_field('cancel_order_action', '_wpnonce_cancel_order');

// In handler:
wp_verify_nonce($_POST['_wpnonce_cancel_order'], 'cancel_order_action')

C. Database Operations Failing

Symptoms: No database changes occur
Fix:

  1. Add error checking after DB operations:
$result = $wpdb->update(...);
if (false === $result) {
    error_log('Update failed: '.$wpdb->last_error);
}

$result = $wpdb->delete(...);
if (false === $result) {
    error_log('Delete failed: '.$wpdb->last_error);
}
  1. Verify table names are correct by checking:
echo "Using table: {$wpdb->prefix}order_items";

D. Redirect Issues

Symptoms: Page doesn't refresh after cancel
Fix:

  1. Ensure no output before redirect:
// At very top of file:
ob_start();

// When redirecting:
ob_end_clean();
wp_redirect($_SERVER['REQUEST_URI']);
exit;

E. Stock Not Restoring

Symptoms: Products disappear but stock doesn't update
Fix:

  1. Verify your stock update query:
$updated = $wpdb->update(
    $table_name_products,
    ['in_stock' => $new_stock],
    ['id' => $product_id],
    ['%d'], // Format for value
    ['%d']  // Format for where
);

Complete Working Solution:

  1. Form Button (in your table row):
<td>
    <form method="post" action="">
        <?php wp_nonce_field('cancel_order_action_'.$order['id'], '_wpnonce_cancel_order'); ?>
        <input type="hidden" name="order_id" value="<?php echo esc_attr($order['id']); ?>">
        <input type="hidden" name="product_id" value="<?php echo esc_attr($order['product_id']); ?>">
        <button type="submit" name="cancel_order" class="remove-item-btn" 
                onclick="return confirm('Remove this item?');">
            <i class="fas fa-times"></i>
        </button>
    </form>
</td>
  1. Cancel Handler:
if (isset($_POST['cancel_order'])) {
    // Verify nonce with order_id for better security
    if (!wp_verify_nonce($_POST['_wpnonce_cancel_order'] ?? '', 'cancel_order_action_'.($_POST['order_id'] ?? 0))) {
        $message = 'Security verification failed!';
    } else {
        $order_id = absint($_POST['order_id'] ?? 0);
        $product_id = absint($_POST['product_id'] ?? 0);
        
        if ($order_id && $product_id) {
            // Get item details
            $item = $wpdb->get_row($wpdb->prepare(
                "SELECT oi.quantity, p.name, p.in_stock 
                 FROM {$table_name_order_items} oi
                 JOIN {$table_name_products} p ON oi.product_id = p.id
                 WHERE oi.order_id = %d AND oi.product_id = %d",
                $order_id, $product_id
            ));
            
            if ($item) {
                // Restore stock
                $wpdb->update(
                    $table_name_products,
                    ['in_stock' => $item->in_stock + $item->quantity],
                    ['id' => $product_id],
                    ['%d'],
                    ['%d']
                );
                
                // Remove item
                $wpdb->delete(
                    $table_name_order_items,
                    ['order_id' => $order_id, 'product_id' => $product_id],
                    ['%d', '%d']
                );
                
                // Check if order is empty
                $remaining = $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(*) FROM {$table_name_order_items} WHERE order_id = %d",
                    $order_id
                ));
                
                if (!$remaining) {
                    $wpdb->delete($table_name_orders, ['id' => $order_id], ['%d']);
                }
                
                wp_safe_redirect(remove_query_arg(['added', 'updated'], wp_get_referer()));
                exit;
            }
        }
    }
}

Still Not Working?

  1. Check your browser's network tab (F12 > Network) to see if the form submits
  2. Verify your database tables exist with the exact names you're using
  3. Check your server error logs for PHP errors
  4. Temporarily disable other plugins to check for conflicts

Would you like me to focus on any specific part of this troubleshooting?*

In the same code, in the "Add orde" form, i want once i input the Product Name, in the "unit Price" (selling price) of that proce will automatically inserted so i wont have to manually type it.