JIT
Getting started and ready to go!
Updated files
v1
the first pasted code is my "Sales and Report" code, add a "Export as PDF" button and code, where i can download and save in the system the Sales Report. The report content is like this, at top center the "ArKi Inventory", next line still at center "Sales Report", next line date when it is exported. And as the main content it shows the table consist the data for Product Name, Date Sold, Category, Unit, Unit Price, Qunatity Sold, Product Revenue, Producty Profit, Total Product Revenue, Total Product Profit. Use the code for Export as PDf from the Inventory Report code. Sales and Report code: <?php session_start(); ?> <?php global $wpdb; // --- Initialize variables --- $search_term = ''; $start_date = ''; // Initialize date variables $end_date = ''; $message = ''; // For success/error messages $message_type = ''; // 'success' or 'error' // --- End Initialization --- // --- Table Names --- $product_table_name = 'products'; // Adjust if needed $category_table_name = 'categories'; // Adjust if needed $orders_table_name = 'orders'; $order_items_table_name = 'order_items'; // --- Handle Delete Product Action --- $upload_dir_info = wp_upload_dir(); $upload_dir = $upload_dir_info['basedir'] . '/product_images/'; $upload_url = $upload_dir_info['baseurl'] . '/product_images/'; if (isset($_GET['action']) && $_GET['action'] === 'delete_product' && isset($_GET['id']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'delete_product_' . absint($_GET['id']))) { $product_id_to_delete = absint($_GET['id']); if ($product_id_to_delete) { // Optional: Delete image file first $image_url_to_delete = $wpdb->get_var($wpdb->prepare("SELECT image_url FROM $product_table_name WHERE id = %d", $product_id_to_delete)); if ($image_url_to_delete) { $image_path = str_replace($upload_url, $upload_dir, $image_url_to_delete); if (file_exists($image_path)) { @unlink($image_path); } } // Delete product record $deleted = $wpdb->delete( $product_table_name, array('id' => $product_id_to_delete), array('%d') ); if ($deleted) { $message = 'Product deleted successfully!'; $message_type = 'success'; $redirect_url = remove_query_arg(array('action', 'id', '_wpnonce')); wp_redirect($redirect_url); exit; } else { $message = 'Error deleting product. It might be linked to existing order items.'; $message_type = 'error'; } } } // --- End Delete Product Action --- // --- Fetch Aggregated Sales Data --- $sales_data = []; $total_revenue = 0; $total_profit = 0; $fetch_error = ''; // Ensure required tables exist before querying $products_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $product_table_name)) == $product_table_name; $orders_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $orders_table_name)) == $orders_table_name; $order_items_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $order_items_table_name)) == $order_items_table_name; if ($products_exists && $orders_exists && $order_items_exists) { // --- SERVER-SIDE SEARCH & DATE FILTER LOGIC --- // Select necessary fields for calculation $base_sql = "SELECT p.id, p.name AS product_name, p.category, p.unit, p.selling_price, p.buying_price, SUM(oi.quantity) AS quantity_sold, MAX(o.updated_at) AS date_sold FROM {$product_table_name} p JOIN {$order_items_table_name} oi ON p.id = oi.product_id JOIN {$orders_table_name} o ON oi.order_id = o.id"; $where_conditions = []; $params = []; // Always filter by 'Paid' status for sales report $where_conditions[] = "o.status = %s"; $params[] = 'Paid'; // Get filter values from URL query parameters $search_term = isset($_GET['product_search']) ? sanitize_text_field(wp_unslash($_GET['product_search'])) : ''; $start_date = isset($_GET['start_date']) ? sanitize_text_field(wp_unslash($_GET['start_date'])) : ''; $end_date = isset($_GET['end_date']) ? sanitize_text_field(wp_unslash($_GET['end_date'])) : ''; // Add search condition if search term is present if (!empty($search_term)) { $search_like = '%' . $wpdb->esc_like($search_term) . '%'; $where_conditions[] = "(p.name LIKE %s OR p.category LIKE %s)"; $params[] = $search_like; $params[] = $search_like; } // Add date range conditions if (!empty($start_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $start_date)) { $where_conditions[] = "o.order_date >= %s"; $params[] = $start_date . ' 00:00:00'; } else { $fetch_error .= " Invalid start date format."; $start_date = ''; } } if (!empty($end_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $end_date)) { $where_conditions[] = "o.order_date <= %s"; $params[] = $end_date . ' 23:59:59'; } else { $fetch_error .= " Invalid end date format."; $end_date = ''; } } $sql = $base_sql; if (!empty($where_conditions)) { $sql .= " WHERE " . implode(' AND ', $where_conditions); } // Group by product to get total quantity sold $sql .= " GROUP BY p.id, p.name, p.category, p.unit, p.selling_price, p.buying_price"; $sql .= " ORDER BY p.name ASC"; if (empty($fetch_error)) { $prepared_sql = $wpdb->prepare($sql, $params); $sales_results = $wpdb->get_results($prepared_sql, ARRAY_A); if ($sales_results === null) { $fetch_error = "Error fetching sales data: " . $wpdb->last_error; } elseif (!empty($sales_results)) { // *** MODIFIED: Calculate Revenue and Profit in PHP based on new definitions *** foreach ($sales_results as $row) { // Calculate Revenue based on current selling price $current_revenue = $row['quantity_sold'] * $row['selling_price']; // Calculate Cost of Goods based on current buying price $cost_of_goods = $row['quantity_sold'] * $row['buying_price']; // Calculate Profit $profit = $current_revenue - $cost_of_goods; $date_sold = new DateTime($row['date_sold']); $formatted_date = $date_sold->format('M j, Y g:i A'); $sales_data[] = [ 'id' => $row['id'], 'product_name' => $row['product_name'], 'date_sold' => $formatted_date, 'category' => $row['category'], 'unit' => $row['unit'], 'unit_price' => $row['selling_price'], // Display current selling price 'quantity_sold' => $row['quantity_sold'], 'product_revenue' => $current_revenue, // Use calculated revenue 'product_profit' => $profit, // Use calculated profit ]; // Accumulate totals based on calculated values $total_revenue += $current_revenue; $total_profit += $profit; } // *** END MODIFICATION *** } } } else { $fetch_error = "Error: One or more required tables (products, orders, order_items) not found."; } $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>Sales Report</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> /* Base & Layout Styles */ 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: #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; } .header { position: fixed; top: 0; left: 240px; right: 0; display: flex; justify-content: space-between; align-items: center; background-color: #77693A; padding: 10px 30px; height: 60px; /* Fixed height */ z-index: 999; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .header-left .date-time { font-size: 15px; /* Slightly smaller */ color: #fff; /* 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: #ffffff; } .header-right .user-info { display: flex; align-items: center; /* Center items horizontally */ gap: 1px; /* Small gap between icon and text */ } .user-dropdown { position: relative; display: inline-block; cursor: pointer; } .user-dropdown-content { display: none; position: absolute; right: 0; background-color: #D1B48C; min-width: 100px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; border-radius: 4px; } .user-dropdown-header { padding: 8px 12px; background-color: ; #D1B48C border-bottom: 1px solid #e9ecef; display: flex; align-items: center; gap: 8px; } .user-dropdown-header i { font-size: 20px; color: #000000; } .user-dropdown-content.user-info { display: flex; align-items: right; gap: 1px; color: #000000; } .user-dropdown-content a { display: flex; padding: 8px 15px; align-items: center; color: #000000; text-decoration: none; font-size: 16px; transition: all 0.2s; } .user-dropdown-content a i { font-size: 16px; /* Adjust logout icon size separately */ margin-right: 8px; /* Space between icon and text */ } .user-dropdown-content a:hover { text-decoration: underline; color: #000000; } .user-dropdown:hover .user-dropdown-content { display: block; } .user-dropdown-content.user-info i { font-size: 16px; align-items: center; } .header-right i { color: #ffffff; /* Icon color */ font-size:40px; /* Larger icon */ /* Remove margin-right when stacked */ } .header-right span { font-size: 15px; /* Smaller font for username */ color: #ffffff; } .main-content { margin-left: 240px; padding: 80px 20px 20px 20px; flex-grow: 1; } .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; } .product-filters-form { display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px; padding: 15px; background-color: #f8f9fa; border: 1px solid #eee; border-radius: 6px; } .filter-group { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; } .product-filters-form label { font-weight: 600; font-size: 0.95em; margin-bottom: 0; white-space: nowrap; } .product-filters-form input[type="text"], .product-filters-form input[type="date"] { padding: 8px 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 0.95em; font-family: inherit; box-sizing: border-box; flex-grow: 1; min-width: 140px; } .search-input-wrapper { position: relative; display: flex; flex-grow: 1; align-items: center; } #product-search { padding-right: 30px; } .product-search-cclear { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); background: none; border: none; font-size: 1.5em; color: #aaa; cursor: pointer; padding: 0 4px; line-height: 1; display: <?php echo !empty($search_term) ? 'inline-block' : 'none'; ?>; transition: color 0.2s ease-in-out; } .product-search-clear:hover { color: #555; } .product-filters-form button[type="submit"] { padding: 8px 15px; background-color: #D1B48C; color: black; border: none; border-radius: 4px; cursor: pointer; font-size: 1em; line-height: 1.5; transition: background-color 0.3s ease; flex-shrink: 0; } .product-filters-form button[type="submit"]:hover { background-color: #be9b7b ; } .sales-container { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border: 1px solid #eee; overflow-x: auto; } .sales-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; flex-wrap: wrap; gap: 10px;} .sales-header h2 { margin: 0; color: #2f4050; font-size: 20px; } #sales-table { width: 100%; border-collapse: collapse; margin-top: 10px; } #sales-table th, #sales-table td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #eee; font-size: 0.95em; vertical-align: middle; } #sales-table th { background-color: #f9f9f9; font-weight: 600; color: #555; white-space: nowrap; } #sales-table tbody tr:hover { background-color: #f5f5f5; } #sales-table th.numeric, #sales-table td.numeric { text-align: right; } #sales-table tfoot td { font-weight: bold; font-size: 1.1em; border-top: 2px solid #333; padding-top: 15px; padding-bottom: 15px; } #sales-table tfoot td.total-label { text-align: right; padding-right: 10px; } #sales-table tfoot td.total-value { text-align: right; } .sales-actions a.delete { display: inline-block; text-align: center; margin-right: 5px; text-decoration: none; padding: 4px 8px; border-radius: 4px; color: #000000; font-size: 0.9em; white-space: nowrap; transition: background-color 0.2s ease; } .sales-actions a.delete i { margin-right: 3px; } </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; ?> </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"> <!-- Updated user info with dropdown --> <div class="user-dropdown"> <div class="user-info"> <span id="current-username-header"> <?php echo esc_html($current_username); ?> <i class="fas fa-user-circle"></i> </span> </div> <div class="user-dropdown-content"> <div class="user-dropdown-header"> <i class="fas fa-user-circle"style="color:#000000;"></i> <span style="color:#000000;"><?php echo esc_html($current_username); ?></span> </div> <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" style="font-size: 16px; color:#000000;"></i>Logout </a> </div> </div> </div> </header> <h1>Sales Reports</h1> <hr/> <?php if ($message): ?> <div class="notice-wrap"> <div class="notice notice-<?php echo esc_attr($message_type); ?>"> <p><?php echo esc_html($message); ?></p> </div> </div> <?php endif; ?> <form method="GET" class="product-filters-form"> <div class="filter-group search-input-wrapper"> <label for="product-search">Search:</label> <input type="text" id="product-search" name="product_search" placeholder="Name or Category..." value="<?php echo esc_attr($search_term); ?>"> </div> <div class="filter-group"> <label for="start-date">Date Sold From:</label> <input type="date" id="start-date" name="start_date" value="<?php echo esc_attr($start_date); ?>"> </div> <div class="filter-group"> <label for="end-date">Date Sold To:</label> <input type="date" id="end-date" name="end_date" value="<?php echo esc_attr($end_date); ?>"> </div> <button type="submit"><i class="fas fa-filter"></i> Filter</button> </form> <div class="sales-container"> <table id="sales-table"> <thead> <tr> <th>Product Name</th> <th>Date Last Sold</th> <th>Category</th> <th>Unit</th> <th class="numeric">Unit Price</th> <th class="numeric">Qty Sold</th> <th class="numeric">Product Revenue</th> <th class="numeric">Product Profit</th> <th class="sales-actions">Action</th> </tr> </thead> <tbody> <?php if (empty($sales_data)): ?> <tr> <td colspan="9" style="text-align: center; color: #666; padding: 20px;"> No sales data found for 'Paid' orders. </td> </tr> <?php else: ?> <?php foreach ($sales_data as $sale): $delete_url = add_query_arg([ 'action' => 'delete_product', 'id' => $sale['id'], '_wpnonce' => wp_create_nonce('delete_product_' . $sale['id']) ], get_permalink()); ?> <tr> <td><?php echo esc_html($sale['product_name']); ?></td> <td><?php echo esc_html($sale['date_sold']); ?></td> <td><?php echo esc_html($sale['category']); ?></td> <td><?php echo esc_html($sale['unit']); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($sale['unit_price'], 2); ?></td> <td class="numeric"><?php echo esc_html($sale['quantity_sold']); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($sale['product_revenue'], 2); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($sale['product_profit'], 2); ?></td> <td class="sales-actions"> <a href="<?php echo esc_url($delete_url); ?>" class="delete" onclick="return confirm('Are you sure you want to delete this product entirely: <?php echo esc_js($sale['product_name']); ?>? This cannot be undone.')"> <i class="fas fa-trash-alt"></i> Delete </a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> <tfoot> <tr> <td colspan="6" class="total-label">Total Product Revenue:</td> <td class="numeric total-value"><?php echo esc_html($currency_symbol); ?><?php echo number_format($total_revenue, 2); ?></td> <td colspan="2"></td> </tr> <tr> <td colspan="7" class="total-label">Total Product Profit:</td> <td class="numeric total-value"><?php echo esc_html($currency_symbol); ?><?php echo number_format($total_profit, 2); ?></td> <td></td> </tr> </tfoot> </table> </div> </main> </div> <!-- JavaScript for Clear Button & Print Button --> <script> document.addEventListener('DOMContentLoaded', function() { console.log("Sales Report Script: DOM Loaded"); // --- Clear Search Button Logic --- const searchInput = document.getElementById('product-search'); const clearButton = document.getElementById('clear-search-btn'); if (searchInput && clearButton) { const toggleClearButton = () => { clearButton.style.display = searchInput.value.trim() !== '' ? 'inline-block' : 'none'; }; searchInput.addEventListener('input', toggleClearButton); clearButton.addEventListener('click', function(event) { event.preventDefault(); searchInput.value = ''; clearButton.style.display = 'none'; const filterForm = searchInput.closest('form'); if (filterForm) { filterForm.submit(); } else { console.error("Clear Button Script: Could not find parent form."); } }); toggleClearButton(); // Initial check console.log("Clear Button Script: Setup Complete."); } else { if (!searchInput) console.warn("Clear Button Script: Could not find search input #product-search."); if (!clearButton) console.warn("Clear Button Script: Could not find clear button #clear-search-btn."); } // --- End Clear Search Button Logic --- }); </script> <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> Inventory Report 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 /** * Plugin Name: Inventory Report Menu Snippet * Description: Displays current inventory levels with search, date filtering, and print functionality. */ global $wpdb; // --- Initialize variables --- $search_term = ''; $start_date = ''; // Initialize date variables $end_date = ''; $message = ''; // For success/error messages (might not be needed here unless adding actions) $message_type = ''; // 'success' or 'error' // --- End Initialization --- // --- Table Names --- $product_table_name = 'products'; // Adjust if needed // --- Fetch Inventory Data --- $inventory_data = []; $total_stock_value = 0; $fetch_error = ''; // Ensure product table exists $products_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $product_table_name)) == $product_table_name; if ($products_exists) { // --- SERVER-SIDE SEARCH & DATE FILTER LOGIC --- // Select all necessary columns from the products table $sql = "SELECT id, name, category, unit, selling_price, in_stock, created_at, updated_at, buying_price FROM {$product_table_name}"; $where_conditions = []; $params = []; // Get filter values from URL query parameters $search_term = isset($_GET['product_search']) ? sanitize_text_field(wp_unslash($_GET['product_search'])) : ''; $start_date = isset($_GET['start_date']) ? sanitize_text_field(wp_unslash($_GET['start_date'])) : ''; $end_date = isset($_GET['end_date']) ? sanitize_text_field(wp_unslash($_GET['end_date'])) : ''; // Add search condition if search term is present if (!empty($search_term)) { $search_like = '%' . $wpdb->esc_like($search_term) . '%'; // Search product name or category $where_conditions[] = "(name LIKE %s OR category LIKE %s)"; $params[] = $search_like; $params[] = $search_like; } // Add date range conditions based on 'created_at' if (!empty($start_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $start_date)) { $where_conditions[] = "created_at >= %s"; // Filter by creation date $params[] = $start_date . ' 00:00:00'; } else { $fetch_error .= " Invalid start date format."; $start_date = ''; } } if (!empty($end_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $end_date)) { $where_conditions[] = "created_at <= %s"; // Filter by creation date $params[] = $end_date . ' 23:59:59'; } else { $fetch_error .= " Invalid end date format."; $end_date = ''; } } if (!empty($where_conditions)) { $sql .= " WHERE " . implode(' AND ', $where_conditions); } $sql .= " ORDER BY name ASC"; // Order by product name if (empty($fetch_error)) { $prepared_sql = $wpdb->prepare($sql, $params); $inventory_results = $wpdb->get_results($prepared_sql, ARRAY_A); if ($inventory_results === null) { $fetch_error = "Error fetching inventory data: " . $wpdb->last_error; } elseif (!empty($inventory_results)) { // Calculate total stock value foreach ($inventory_results as $item) { $stock_value = $item['in_stock'] * $item['buying_price']; $total_stock_value += $stock_value; // Add the calculated value if needed for display per item (optional) // $item['stock_value_at_cost'] = $stock_value; $inventory_data[] = $item; // Add row to data array } } } } else { $fetch_error = "Error: Products table '{$product_table_name}' not found."; } // Get currency symbol $currency_symbol = function_exists('get_woocommerce_currency_symbol') ? get_woocommerce_currency_symbol() : '₱'; // Handle delete product action if (isset($_GET['delete_product']) && isset($_GET['id']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'delete_product_' . $_GET['id'])) { $product_id = intval($_GET['id']); $deleted = $wpdb->delete( $product_table_name, ['id' => $product_id], ['%d'] ); if ($deleted) { $message = "Product deleted successfully."; $message_type = 'success'; // Redirect to clear query params header("Location: " . remove_query_arg(['delete_product', 'id', '_wpnonce'])); exit; } else { $message = "Error deleting product."; $message_type = 'error'; } } // Handle clear all products action (for admin only) if ($current_username === 'admin' && isset($_GET['clear_all_products']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'clear_all_products_action')) { $cleared = $wpdb->query("TRUNCATE TABLE $product_table_name"); if ($cleared !== false) { $message = "All products cleared successfully."; $message_type = 'success'; // Redirect to clear query params header("Location: " . remove_query_arg(['clear_all_products', '_wpnonce'])); exit; } else { $message = "Error clearing all products."; $message_type = 'error'; } } // Handle PDF export if (isset($_GET['export_pdf'])) { // Clear any previous output if (ob_get_level()) { ob_end_clean(); } // Path to dompdf - adjust if you placed it elsewhere $dompdf_path = ABSPATH . 'wp-content/plugins/dompdf/autoload.inc.php'; if (!file_exists($dompdf_path)) { die('Dompdf library not found at: ' . $dompdf_path); } require_once($dompdf_path); try { // Create PDF document with improved settings $options = new \Dompdf\Options(); $options->set('isRemoteEnabled', true); $options->set('isHtml5ParserEnabled', true); $options->set('isPhpEnabled', true); $options->set('defaultFont', 'helvetica'); $options->set('isFontSubsettingEnabled', true); $dompdf = new \Dompdf\Dompdf($options); // Get current date in Philippines timezone $timezone = new DateTimeZone('Asia/Manila'); $export_date = (new DateTime('now', $timezone))->format('M j, Y g:i A'); // Build HTML content with simplified styling $html = ' <style> body { font-family: Arial, sans-serif; margin: 15px; } h1 { text-align: center; color: #333; margin-bottom: 5px; } h2 { text-align: center; color: #555; margin-top: 0; } table { width: 100%; border-collapse: collapse; margin-top: 15px; } th { background-color: #D1B48C; color: #000; padding: 8px; text-align: left; } td { padding: 8px; border-bottom: 1px solid #ddd; } .text-right { text-align: right; } .total-row { font-weight: bold; background-color: #f5f5f5; } </style> <h1>ArKi Inventory</h1> <h2>Inventory Report</h2> <p style="text-align: center;">Exported on: ' . $export_date . '</p> <table> <thead> <tr> <th>Product Name</th> <th>Category</th> <th>Unit</th> <th class="text-right">Cost Price</th> <th class="text-right">Retail Price</th> <th class="text-right">Stock</th> <th>Date Added</th> <th>Date Updated</th> <th class="text-right">Stock Value</th> </tr> </thead> <tbody>'; $total_stock_value = 0; foreach ($inventory_data as $item) { $stock_value = $item['in_stock'] * $item['buying_price']; $total_stock_value += $stock_value; $html .= ' <tr> <td>' . htmlspecialchars($item['name']) . '</td> <td>' . htmlspecialchars($item['category']) . '</td> <td>' . htmlspecialchars($item['unit']) . '</td> <td class="text-right">&#8369;' . number_format($item['buying_price'], 2) . '</td> <td class="text-right">&#8369;' . number_format($item['selling_price'], 2) . '</td> <td class="text-right">' . htmlspecialchars($item['in_stock']) . '</td> <td>' . htmlspecialchars(date('M j, Y', strtotime($item['created_at']))) . '</td> <td>' . htmlspecialchars(date('M j, Y', strtotime($item['updated_at']))) . '</td> <td class="text-right">&#8369;' . number_format($stock_value, 2) . '</td> </tr>'; } $html .= ' <tr class="total-row"> <td colspan="8" class="text-right">Total Stock Value (Cost):</td> <td class="text-right">&#8369;' . number_format($total_stock_value, 2) . '</td> </tr> </tbody> </table>'; // Load HTML to dompdf $dompdf->loadHtml($html); // Set paper size and orientation $dompdf->setPaper('A4', 'landscape'); // Render the HTML as PDF $dompdf->render(); // Generate a filename $filename = 'ArKi-Inventory-Report_' . date('Y-m-d') . '.pdf'; // Output the PDF with proper headers header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Transfer-Encoding: binary'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); echo $dompdf->output(); exit; } catch (Exception $e) { die('Error generating PDF: ' . $e->getMessage()); } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Inventory Report</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.13/jspdf.plugin.autotable.min.js"></script> <!-- Initialize jsPDF --> <script> window.jsPDF = window.jspdf.jsPDF; </script> <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> /* Base & Layout Styles - Flexbox Fix for Sidebar Overlap - VERSION 4 - Flex Basis and Max-Width */ 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; flex-basis: 240px; /* Use flex-basis instead of width */ /* ADDED */ /* Prevent sidebar from becoming wider */ /* ADDED */ 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; flex-shrink: 0; /* Prevent sidebar from shrinking */ } .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; } .header { position: fixed; top: 0; left: 240px; right: 0; display: flex; justify-content: space-between; align-items: center; background-color: #77693A; padding: 10px 30px; height: 60px; /* Fixed height */ z-index: 999; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .header-left .date-time { font-size: 15px; /* Slightly smaller */ color: #fff; /* 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: #ffffff; } /* 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 */ } .user-dropdown { position: relative; display: inline-block; cursor: pointer; } .user-dropdown-content { display: none; position: absolute; right: 0; background-color: #D1B48C; min-width: 100px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; border-radius: 4px; } .user-dropdown-header { padding: 8px 12px; background-color: ; #D1B48C border-bottom: 1px solid #e9ecef; display: flex; align-items: center; gap: 8px; } .user-dropdown-header i { font-size: 20px; color: #000000; } .user-dropdown-content.user-info { display: flex; align-items: right; gap: 1px; color: #000000; } .user-dropdown-content a { display: flex; padding: 8px 15px; align-items: center; color: #000000; text-decoration: none; font-size: 16px; transition: all 0.2s; } .user-dropdown-content a i { font-size: 16px; /* Adjust logout icon size separately */ margin-right: 8px; /* Space between icon and text */ } .user-dropdown-content a:hover { text-decoration: underline; color: #000000; } .user-dropdown:hover .user-dropdown-content { display: block; } .user-dropdown-content.user-info i { font-size: 16px; align-items: center; } .header-right i { color: #ffffff; /* Icon color */ font-size:40px; /* Larger icon */ /* Remove margin-right when stacked */ } .header-right span { font-size: 15px; /* Smaller font for username */ color: #ffffff; } .main-content { margin-left: 240px; padding: 80px 20px 20px 20px; flex-grow: 1; } .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; } .product-filters-form { display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px; padding: 15px; background-color: #f8f9fa; border: 1px solid #eee; border-radius: 6px; } .filter-group { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; } .product-filters-form label { font-weight: 600; font-size: 0.95em; margin-bottom: 0; white-space: nowrap; } .product-filters-form input[type="text"], .product-filters-form input[type="date"] { padding: 8px 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 0.95em; font-family: inherit; box-sizing: border-box; flex-grow: 1; min-width: 140px; } .search-input-wrapper { position: relative; display: flex; flex-grow: 1; align-items: center; } #product-search { padding-right: 30px; } .product-search-clear { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); background: none; border: none; font-size: 1.5em; color: #aaa; cursor: pointer; padding: 0 4px; line-height: 1; display: <?php echo !empty($search_term) ? 'inline-block' : 'none'; ?>; transition: color 0.2s ease-in-out; } .product-search-clear:hover { color: #555; } .product-filters-form button[type="submit"] { padding: 8px 15px; background-color: #D1B48C; color: black; border: none; border-radius: 4px; cursor: pointer; font-size: 1em; line-height: 1.5; transition: background-color 0.3s ease; flex-shrink: 0; } .product-filters-form button[type="submit"]:hover { background-color: #be9b7b ; } .inventory-container { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border: 1px solid #eee; overflow-x: auto; } .report-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; flex-wrap: wrap; gap: 10px;} .report-header h2 { margin: 0; color: #2f4050; font-size: 20px; } #inventory-table { width: 100%; border-collapse: collapse; margin-top: 10px; table-layout: fixed; } #inventory-table th, #inventory-table td { padding: 10px 12px; text-align: center; border-bottom: 1px solid #eee; font-size: 0.95em; vertical-align: middle; word-wrap: break-word; } #inventory-table th { background-color: #f9f9f9; font-weight: 600; color: #555; white-space: nowrap; } #inventory-table tbody tr:hover { background-color: #f5f5f5; } #inventory-table th.numeric, #inventory-table td.numeric { text-align: center; } #inventory-table td.low-stock { background-color: #f8d7da; color: #721c24; } #inventory-table tfoot td { font-weight: bold; font-size: 1.1em; border-top: 2px solid #333; padding-top: 15px; padding-bottom: 15px; } #inventory-table tfoot td.total-label { text-align: right; padding-right: 10px; } #inventory-table tfoot td.total-value { text-align: right; } .delete-button { color: black; padding: 5px; text-decoration: none; display: inline-block; transition: color 0.2s; } .clear-all-button { margin-bottom: 1px; /* Adds space below the button */ margin-left: auto; /* Ensures the button pushes itself to the right */ display: block; /* Ensures the button behaves as a block element */ font-weight: 500; padding: 8px 15px; background-color: #D1B48C; color: #0000000; text-decoration: none; border-radius: 4px; font-size: 16px; transition: background-color 0.3s; line-height: 0.5; font-weight: 500; } } </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; ?> </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"> <!-- Updated user info with dropdown --> <div class="user-dropdown"> <div class="user-info"> <span id="current-username-header"> <?php echo esc_html($current_username); ?> <i class="fas fa-user-circle"></i> </span> </div> <div class="user-dropdown-content"> <div class="user-dropdown-header"> <i class="fas fa-user-circle"style="color:#000000;"></i> <span style="color:#000000;"><?php echo esc_html($current_username); ?></span> </div> <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" style="font-size: 16px; color:#000000;"></i>Logout </a> </div> </div> </div> </header> <h1>Inventory Report</h1> <hr/> </header> <?php if ($message): ?> <div class="notice-wrap"> <div class="notice notice-<?php echo esc_attr($message_type); ?>"> <p><?php echo esc_html($message); ?></p> </div> </div> <?php endif; ?> <?php if (!empty($fetch_error)): ?> <div class="notice-wrap"> <div class="notice notice-error"> <p><?php echo esc_html($fetch_error); ?></p> </div> </div> <?php endif; ?> <div class="inventory-container"> <!-- Filter Form --> <form method="GET" class="product-filters-form"> <div class="filter-group search-input-wrapper"> <label for="product-search">Search:</label> <input type="text" id="product-search" name="product_search" placeholder="Name or Category..." value="<?php echo esc_attr($search_term); ?>"> <button type="button" class="product-search-clear" id="clear-search-btn" title="Clear search">&times;</button> </div> <div class="filter-group"> <label for="start-date">Date Added From:</label> <input type="date" id="start-date" name="start_date" value="<?php echo esc_attr($start_date); ?>"> </div> <div class="filter-group"> <label for="end-date">Date Added To:</label> <input type="date" id="end-date" name="end_date" value="<?php echo esc_attr($end_date); ?>"> </div> <button type="submit"><i class="fas fa-filter"></i> Filter</button> </form> <!-- End Filter Form --> <div id="inventory-report-content"> <div class="report-header"> <h2>Current Inventory Status</h2> <?php if ($current_username === 'admin'): ?> <a href="?export_pdf=1" style= " display: inline-block; padding: 8px 15px;background-color: #d9534f;color: #0000000;text-decoration: none;border-radius: 4px;margin-right: 10px;font-size: 16px;transition: background-color 0.3s;line-height: 1;display: flex;gap: 10px;font-weight: 500;"> Export as PDF </a> <a href="<?php echo esc_url(wp_nonce_url(add_query_arg(['clear_all_products' => 'true']), 'clear_all_products_action')); ?>" class="clear-all-button" onclick="return confirm('WARNING: This will delete ALL products. Are you sure?')"> <i class="fas fa-trash"></i> Clear All </a> </div> <?php endif; ?> </div> <table id="inventory-table"> <thead> <tr> <th>Product Name</th> <th>Category</th> <th>Unit</th> <th class="numeric">Cost Price</th> <th class="numeric">Retail Price</th> <th class="numeric">Current Stock</th> <th>Date Added</th> <th>Last Updated</th> <th class="numeric">Stock Value (Cost)</th> <th>Action</th> </tr> </thead> <tbody> <?php if (empty($inventory_data) && empty($fetch_error)): ?> <tr> <td colspan="8" style="text-align: center; color: #666; padding: 20px;"> <?php echo (!empty($search_term) || !empty($start_date) || !empty($end_date)) ? 'No products match your filters.' : 'No products found in inventory.'; ?> </td> </tr> <?php elseif (!empty($inventory_data)): ?> <?php $low_stock_threshold = 5; // Define your low stock threshold here foreach ($inventory_data as $item): $stock_value = $item['in_stock'] * $item['buying_price']; $is_low_stock = $item['in_stock'] <= $low_stock_threshold && $item['in_stock'] > 0; ?> <tr class="<?php echo $is_low_stock ? 'low-stock' : ''; ?>"> <td><?php echo esc_html($item['name']); ?></td> <td><?php echo esc_html($item['category']); ?></td> <td><?php echo esc_html($item['unit']); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($item['buying_price'], 2); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($item['selling_price'], 2); ?></td> <td class="numeric"><?php echo esc_html($item['in_stock']); ?></td> <td><?php echo esc_html(mysql2date(get_option('date_format'), $item['created_at'])); ?></td> <td><?php echo esc_html(mysql2date(get_option('date_format'), $item['updated_at'])); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($stock_value, 2); ?></td> <td> <a href="<?php echo esc_url(wp_nonce_url(add_query_arg(['delete_product' => 'true', 'id' => $item['id']]), 'delete_product_' . $item['id'])); ?>" class="delete-button" onclick="return confirm('Are you sure you want to delete this product?')"> <i class="fas fa-trash"></i>Delete </a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> <tfoot> <tr> <td colspan="7" class="total-label">Total Stock Value (at Cost):</td> <td class="numeric total-value"><?php echo esc_html($currency_symbol); ?><?php echo number_format($total_stock_value, 2); ?></td> </tr> </tfoot> </table> </div> <!-- End #inventory-report-content --> </div> <!-- End .inventory-container --> </main> </div> <script> document.addEventListener('DOMContentLoaded', function() { console.log("Inventory Report Script: DOM Loaded"); // --- Clear Search Button Logic (No changes) --- const searchInput = document.getElementById('product-search'); const clearButton = document.getElementById('clear-search-btn'); if (searchInput && clearButton) { const toggleClearButton = () => { clearButton.style.display = searchInput.value.trim() !== '' ? 'inline-block' : 'none'; }; searchInput.addEventListener('input', toggleClearButton); clearButton.addEventListener('click', function(event) { event.preventDefault(); searchInput.value = ''; clearButton.style.display = 'none'; const filterForm = searchInput.closest('form'); if (filterForm) { filterForm.submit(); } else { console.error("Clear Button Script: Could not find parent form."); } }); toggleClearButton(); // Initial check console.log("Clear Button Script: Setup Complete."); } else { if (!searchInput) console.warn("Clear Button Script: Could not find search input #product-search."); if (!clearButton) console.warn("Clear Button Script: Could not find clear button #clear-search-btn."); } // --- End Clear Search Button Logic --- </script> <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> <?php // End of PHP Snippet ?>
the first pasted code is my "Sales and Report" code, add a "Export as PDF" button (place it in upper right side inside of the container with sales table)and code, where i can download and save in the system the Sales Report. The report content is like this, at top center the "ArKi Inventory", next line still at center "Sales Report", next line date when it is exported. And as the main content it shows the table consist the data for Product Name, Date Sold, Category, Unit, Unit Price, Qunatity Sold, Product Revenue, Producty Profit, Total Product Revenue, Total Product Profit. Use the code for Export as PDf from the Inventory Report code. Sales and Report code: <?php session_start(); ?> <?php global $wpdb; // --- Initialize variables --- $search_term = ''; $start_date = ''; // Initialize date variables $end_date = ''; $message = ''; // For success/error messages $message_type = ''; // 'success' or 'error' // --- End Initialization --- // --- Table Names --- $product_table_name = 'products'; // Adjust if needed $category_table_name = 'categories'; // Adjust if needed $orders_table_name = 'orders'; $order_items_table_name = 'order_items'; // --- Handle Delete Product Action --- $upload_dir_info = wp_upload_dir(); $upload_dir = $upload_dir_info['basedir'] . '/product_images/'; $upload_url = $upload_dir_info['baseurl'] . '/product_images/'; if (isset($_GET['action']) && $_GET['action'] === 'delete_product' && isset($_GET['id']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'delete_product_' . absint($_GET['id']))) { $product_id_to_delete = absint($_GET['id']); if ($product_id_to_delete) { // Optional: Delete image file first $image_url_to_delete = $wpdb->get_var($wpdb->prepare("SELECT image_url FROM $product_table_name WHERE id = %d", $product_id_to_delete)); if ($image_url_to_delete) { $image_path = str_replace($upload_url, $upload_dir, $image_url_to_delete); if (file_exists($image_path)) { @unlink($image_path); } } // Delete product record $deleted = $wpdb->delete( $product_table_name, array('id' => $product_id_to_delete), array('%d') ); if ($deleted) { $message = 'Product deleted successfully!'; $message_type = 'success'; $redirect_url = remove_query_arg(array('action', 'id', '_wpnonce')); wp_redirect($redirect_url); exit; } else { $message = 'Error deleting product. It might be linked to existing order items.'; $message_type = 'error'; } } } // --- End Delete Product Action --- // --- Fetch Aggregated Sales Data --- $sales_data = []; $total_revenue = 0; $total_profit = 0; $fetch_error = ''; // Ensure required tables exist before querying $products_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $product_table_name)) == $product_table_name; $orders_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $orders_table_name)) == $orders_table_name; $order_items_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $order_items_table_name)) == $order_items_table_name; if ($products_exists && $orders_exists && $order_items_exists) { // --- SERVER-SIDE SEARCH & DATE FILTER LOGIC --- // Select necessary fields for calculation $base_sql = "SELECT p.id, p.name AS product_name, p.category, p.unit, p.selling_price, p.buying_price, SUM(oi.quantity) AS quantity_sold, MAX(o.updated_at) AS date_sold FROM {$product_table_name} p JOIN {$order_items_table_name} oi ON p.id = oi.product_id JOIN {$orders_table_name} o ON oi.order_id = o.id"; $where_conditions = []; $params = []; // Always filter by 'Paid' status for sales report $where_conditions[] = "o.status = %s"; $params[] = 'Paid'; // Get filter values from URL query parameters $search_term = isset($_GET['product_search']) ? sanitize_text_field(wp_unslash($_GET['product_search'])) : ''; $start_date = isset($_GET['start_date']) ? sanitize_text_field(wp_unslash($_GET['start_date'])) : ''; $end_date = isset($_GET['end_date']) ? sanitize_text_field(wp_unslash($_GET['end_date'])) : ''; // Add search condition if search term is present if (!empty($search_term)) { $search_like = '%' . $wpdb->esc_like($search_term) . '%'; $where_conditions[] = "(p.name LIKE %s OR p.category LIKE %s)"; $params[] = $search_like; $params[] = $search_like; } // Add date range conditions if (!empty($start_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $start_date)) { $where_conditions[] = "o.order_date >= %s"; $params[] = $start_date . ' 00:00:00'; } else { $fetch_error .= " Invalid start date format."; $start_date = ''; } } if (!empty($end_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $end_date)) { $where_conditions[] = "o.order_date <= %s"; $params[] = $end_date . ' 23:59:59'; } else { $fetch_error .= " Invalid end date format."; $end_date = ''; } } $sql = $base_sql; if (!empty($where_conditions)) { $sql .= " WHERE " . implode(' AND ', $where_conditions); } // Group by product to get total quantity sold $sql .= " GROUP BY p.id, p.name, p.category, p.unit, p.selling_price, p.buying_price"; $sql .= " ORDER BY p.name ASC"; if (empty($fetch_error)) { $prepared_sql = $wpdb->prepare($sql, $params); $sales_results = $wpdb->get_results($prepared_sql, ARRAY_A); if ($sales_results === null) { $fetch_error = "Error fetching sales data: " . $wpdb->last_error; } elseif (!empty($sales_results)) { // *** MODIFIED: Calculate Revenue and Profit in PHP based on new definitions *** foreach ($sales_results as $row) { // Calculate Revenue based on current selling price $current_revenue = $row['quantity_sold'] * $row['selling_price']; // Calculate Cost of Goods based on current buying price $cost_of_goods = $row['quantity_sold'] * $row['buying_price']; // Calculate Profit $profit = $current_revenue - $cost_of_goods; $date_sold = new DateTime($row['date_sold']); $formatted_date = $date_sold->format('M j, Y g:i A'); $sales_data[] = [ 'id' => $row['id'], 'product_name' => $row['product_name'], 'date_sold' => $formatted_date, 'category' => $row['category'], 'unit' => $row['unit'], 'unit_price' => $row['selling_price'], // Display current selling price 'quantity_sold' => $row['quantity_sold'], 'product_revenue' => $current_revenue, // Use calculated revenue 'product_profit' => $profit, // Use calculated profit ]; // Accumulate totals based on calculated values $total_revenue += $current_revenue; $total_profit += $profit; } // *** END MODIFICATION *** } } } else { $fetch_error = "Error: One or more required tables (products, orders, order_items) not found."; } $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>Sales Report</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> /* Base & Layout Styles */ 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: #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; } .header { position: fixed; top: 0; left: 240px; right: 0; display: flex; justify-content: space-between; align-items: center; background-color: #77693A; padding: 10px 30px; height: 60px; /* Fixed height */ z-index: 999; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .header-left .date-time { font-size: 15px; /* Slightly smaller */ color: #fff; /* 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: #ffffff; } .header-right .user-info { display: flex; align-items: center; /* Center items horizontally */ gap: 1px; /* Small gap between icon and text */ } .user-dropdown { position: relative; display: inline-block; cursor: pointer; } .user-dropdown-content { display: none; position: absolute; right: 0; background-color: #D1B48C; min-width: 100px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; border-radius: 4px; } .user-dropdown-header { padding: 8px 12px; background-color: ; #D1B48C border-bottom: 1px solid #e9ecef; display: flex; align-items: center; gap: 8px; } .user-dropdown-header i { font-size: 20px; color: #000000; } .user-dropdown-content.user-info { display: flex; align-items: right; gap: 1px; color: #000000; } .user-dropdown-content a { display: flex; padding: 8px 15px; align-items: center; color: #000000; text-decoration: none; font-size: 16px; transition: all 0.2s; } .user-dropdown-content a i { font-size: 16px; /* Adjust logout icon size separately */ margin-right: 8px; /* Space between icon and text */ } .user-dropdown-content a:hover { text-decoration: underline; color: #000000; } .user-dropdown:hover .user-dropdown-content { display: block; } .user-dropdown-content.user-info i { font-size: 16px; align-items: center; } .header-right i { color: #ffffff; /* Icon color */ font-size:40px; /* Larger icon */ /* Remove margin-right when stacked */ } .header-right span { font-size: 15px; /* Smaller font for username */ color: #ffffff; } .main-content { margin-left: 240px; padding: 80px 20px 20px 20px; flex-grow: 1; } .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; } .product-filters-form { display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px; padding: 15px; background-color: #f8f9fa; border: 1px solid #eee; border-radius: 6px; } .filter-group { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; } .product-filters-form label { font-weight: 600; font-size: 0.95em; margin-bottom: 0; white-space: nowrap; } .product-filters-form input[type="text"], .product-filters-form input[type="date"] { padding: 8px 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 0.95em; font-family: inherit; box-sizing: border-box; flex-grow: 1; min-width: 140px; } .search-input-wrapper { position: relative; display: flex; flex-grow: 1; align-items: center; } #product-search { padding-right: 30px; } .product-search-cclear { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); background: none; border: none; font-size: 1.5em; color: #aaa; cursor: pointer; padding: 0 4px; line-height: 1; display: <?php echo !empty($search_term) ? 'inline-block' : 'none'; ?>; transition: color 0.2s ease-in-out; } .product-search-clear:hover { color: #555; } .product-filters-form button[type="submit"] { padding: 8px 15px; background-color: #D1B48C; color: black; border: none; border-radius: 4px; cursor: pointer; font-size: 1em; line-height: 1.5; transition: background-color 0.3s ease; flex-shrink: 0; } .product-filters-form button[type="submit"]:hover { background-color: #be9b7b ; } .sales-container { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border: 1px solid #eee; overflow-x: auto; } .sales-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; flex-wrap: wrap; gap: 10px;} .sales-header h2 { margin: 0; color: #2f4050; font-size: 20px; } #sales-table { width: 100%; border-collapse: collapse; margin-top: 10px; } #sales-table th, #sales-table td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #eee; font-size: 0.95em; vertical-align: middle; } #sales-table th { background-color: #f9f9f9; font-weight: 600; color: #555; white-space: nowrap; } #sales-table tbody tr:hover { background-color: #f5f5f5; } #sales-table th.numeric, #sales-table td.numeric { text-align: right; } #sales-table tfoot td { font-weight: bold; font-size: 1.1em; border-top: 2px solid #333; padding-top: 15px; padding-bottom: 15px; } #sales-table tfoot td.total-label { text-align: right; padding-right: 10px; } #sales-table tfoot td.total-value { text-align: right; } .sales-actions a.delete { display: inline-block; text-align: center; margin-right: 5px; text-decoration: none; padding: 4px 8px; border-radius: 4px; color: #000000; font-size: 0.9em; white-space: nowrap; transition: background-color 0.2s ease; } .sales-actions a.delete i { margin-right: 3px; } </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; ?> </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"> <!-- Updated user info with dropdown --> <div class="user-dropdown"> <div class="user-info"> <span id="current-username-header"> <?php echo esc_html($current_username); ?> <i class="fas fa-user-circle"></i> </span> </div> <div class="user-dropdown-content"> <div class="user-dropdown-header"> <i class="fas fa-user-circle"style="color:#000000;"></i> <span style="color:#000000;"><?php echo esc_html($current_username); ?></span> </div> <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" style="font-size: 16px; color:#000000;"></i>Logout </a> </div> </div> </div> </header> <h1>Sales Reports</h1> <hr/> <?php if ($message): ?> <div class="notice-wrap"> <div class="notice notice-<?php echo esc_attr($message_type); ?>"> <p><?php echo esc_html($message); ?></p> </div> </div> <?php endif; ?> <form method="GET" class="product-filters-form"> <div class="filter-group search-input-wrapper"> <label for="product-search">Search:</label> <input type="text" id="product-search" name="product_search" placeholder="Name or Category..." value="<?php echo esc_attr($search_term); ?>"> </div> <div class="filter-group"> <label for="start-date">Date Sold From:</label> <input type="date" id="start-date" name="start_date" value="<?php echo esc_attr($start_date); ?>"> </div> <div class="filter-group"> <label for="end-date">Date Sold To:</label> <input type="date" id="end-date" name="end_date" value="<?php echo esc_attr($end_date); ?>"> </div> <button type="submit"><i class="fas fa-filter"></i> Filter</button> </form> <div class="sales-container"> <table id="sales-table"> <thead> <tr> <th>Product Name</th> <th>Date Last Sold</th> <th>Category</th> <th>Unit</th> <th class="numeric">Unit Price</th> <th class="numeric">Qty Sold</th> <th class="numeric">Product Revenue</th> <th class="numeric">Product Profit</th> <th class="sales-actions">Action</th> </tr> </thead> <tbody> <?php if (empty($sales_data)): ?> <tr> <td colspan="9" style="text-align: center; color: #666; padding: 20px;"> No sales data found for 'Paid' orders. </td> </tr> <?php else: ?> <?php foreach ($sales_data as $sale): $delete_url = add_query_arg([ 'action' => 'delete_product', 'id' => $sale['id'], '_wpnonce' => wp_create_nonce('delete_product_' . $sale['id']) ], get_permalink()); ?> <tr> <td><?php echo esc_html($sale['product_name']); ?></td> <td><?php echo esc_html($sale['date_sold']); ?></td> <td><?php echo esc_html($sale['category']); ?></td> <td><?php echo esc_html($sale['unit']); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($sale['unit_price'], 2); ?></td> <td class="numeric"><?php echo esc_html($sale['quantity_sold']); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($sale['product_revenue'], 2); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($sale['product_profit'], 2); ?></td> <td class="sales-actions"> <a href="<?php echo esc_url($delete_url); ?>" class="delete" onclick="return confirm('Are you sure you want to delete this product entirely: <?php echo esc_js($sale['product_name']); ?>? This cannot be undone.')"> <i class="fas fa-trash-alt"></i> Delete </a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> <tfoot> <tr> <td colspan="6" class="total-label">Total Product Revenue:</td> <td class="numeric total-value"><?php echo esc_html($currency_symbol); ?><?php echo number_format($total_revenue, 2); ?></td> <td colspan="2"></td> </tr> <tr> <td colspan="7" class="total-label">Total Product Profit:</td> <td class="numeric total-value"><?php echo esc_html($currency_symbol); ?><?php echo number_format($total_profit, 2); ?></td> <td></td> </tr> </tfoot> </table> </div> </main> </div> <!-- JavaScript for Clear Button & Print Button --> <script> document.addEventListener('DOMContentLoaded', function() { console.log("Sales Report Script: DOM Loaded"); // --- Clear Search Button Logic --- const searchInput = document.getElementById('product-search'); const clearButton = document.getElementById('clear-search-btn'); if (searchInput && clearButton) { const toggleClearButton = () => { clearButton.style.display = searchInput.value.trim() !== '' ? 'inline-block' : 'none'; }; searchInput.addEventListener('input', toggleClearButton); clearButton.addEventListener('click', function(event) { event.preventDefault(); searchInput.value = ''; clearButton.style.display = 'none'; const filterForm = searchInput.closest('form'); if (filterForm) { filterForm.submit(); } else { console.error("Clear Button Script: Could not find parent form."); } }); toggleClearButton(); // Initial check console.log("Clear Button Script: Setup Complete."); } else { if (!searchInput) console.warn("Clear Button Script: Could not find search input #product-search."); if (!clearButton) console.warn("Clear Button Script: Could not find clear button #clear-search-btn."); } // --- End Clear Search Button Logic --- }); </script> <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> Inventory Report 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 /** * Plugin Name: Inventory Report Menu Snippet * Description: Displays current inventory levels with search, date filtering, and print functionality. */ global $wpdb; // --- Initialize variables --- $search_term = ''; $start_date = ''; // Initialize date variables $end_date = ''; $message = ''; // For success/error messages (might not be needed here unless adding actions) $message_type = ''; // 'success' or 'error' // --- End Initialization --- // --- Table Names --- $product_table_name = 'products'; // Adjust if needed // --- Fetch Inventory Data --- $inventory_data = []; $total_stock_value = 0; $fetch_error = ''; // Ensure product table exists $products_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $product_table_name)) == $product_table_name; if ($products_exists) { // --- SERVER-SIDE SEARCH & DATE FILTER LOGIC --- // Select all necessary columns from the products table $sql = "SELECT id, name, category, unit, selling_price, in_stock, created_at, updated_at, buying_price FROM {$product_table_name}"; $where_conditions = []; $params = []; // Get filter values from URL query parameters $search_term = isset($_GET['product_search']) ? sanitize_text_field(wp_unslash($_GET['product_search'])) : ''; $start_date = isset($_GET['start_date']) ? sanitize_text_field(wp_unslash($_GET['start_date'])) : ''; $end_date = isset($_GET['end_date']) ? sanitize_text_field(wp_unslash($_GET['end_date'])) : ''; // Add search condition if search term is present if (!empty($search_term)) { $search_like = '%' . $wpdb->esc_like($search_term) . '%'; // Search product name or category $where_conditions[] = "(name LIKE %s OR category LIKE %s)"; $params[] = $search_like; $params[] = $search_like; } // Add date range conditions based on 'created_at' if (!empty($start_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $start_date)) { $where_conditions[] = "created_at >= %s"; // Filter by creation date $params[] = $start_date . ' 00:00:00'; } else { $fetch_error .= " Invalid start date format."; $start_date = ''; } } if (!empty($end_date)) { if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $end_date)) { $where_conditions[] = "created_at <= %s"; // Filter by creation date $params[] = $end_date . ' 23:59:59'; } else { $fetch_error .= " Invalid end date format."; $end_date = ''; } } if (!empty($where_conditions)) { $sql .= " WHERE " . implode(' AND ', $where_conditions); } $sql .= " ORDER BY name ASC"; // Order by product name if (empty($fetch_error)) { $prepared_sql = $wpdb->prepare($sql, $params); $inventory_results = $wpdb->get_results($prepared_sql, ARRAY_A); if ($inventory_results === null) { $fetch_error = "Error fetching inventory data: " . $wpdb->last_error; } elseif (!empty($inventory_results)) { // Calculate total stock value foreach ($inventory_results as $item) { $stock_value = $item['in_stock'] * $item['buying_price']; $total_stock_value += $stock_value; // Add the calculated value if needed for display per item (optional) // $item['stock_value_at_cost'] = $stock_value; $inventory_data[] = $item; // Add row to data array } } } } else { $fetch_error = "Error: Products table '{$product_table_name}' not found."; } // Get currency symbol $currency_symbol = function_exists('get_woocommerce_currency_symbol') ? get_woocommerce_currency_symbol() : '₱'; // Handle delete product action if (isset($_GET['delete_product']) && isset($_GET['id']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'delete_product_' . $_GET['id'])) { $product_id = intval($_GET['id']); $deleted = $wpdb->delete( $product_table_name, ['id' => $product_id], ['%d'] ); if ($deleted) { $message = "Product deleted successfully."; $message_type = 'success'; // Redirect to clear query params header("Location: " . remove_query_arg(['delete_product', 'id', '_wpnonce'])); exit; } else { $message = "Error deleting product."; $message_type = 'error'; } } // Handle clear all products action (for admin only) if ($current_username === 'admin' && isset($_GET['clear_all_products']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'clear_all_products_action')) { $cleared = $wpdb->query("TRUNCATE TABLE $product_table_name"); if ($cleared !== false) { $message = "All products cleared successfully."; $message_type = 'success'; // Redirect to clear query params header("Location: " . remove_query_arg(['clear_all_products', '_wpnonce'])); exit; } else { $message = "Error clearing all products."; $message_type = 'error'; } } // Handle PDF export if (isset($_GET['export_pdf'])) { // Clear any previous output if (ob_get_level()) { ob_end_clean(); } // Path to dompdf - adjust if you placed it elsewhere $dompdf_path = ABSPATH . 'wp-content/plugins/dompdf/autoload.inc.php'; if (!file_exists($dompdf_path)) { die('Dompdf library not found at: ' . $dompdf_path); } require_once($dompdf_path); try { // Create PDF document with improved settings $options = new \Dompdf\Options(); $options->set('isRemoteEnabled', true); $options->set('isHtml5ParserEnabled', true); $options->set('isPhpEnabled', true); $options->set('defaultFont', 'helvetica'); $options->set('isFontSubsettingEnabled', true); $dompdf = new \Dompdf\Dompdf($options); // Get current date in Philippines timezone $timezone = new DateTimeZone('Asia/Manila'); $export_date = (new DateTime('now', $timezone))->format('M j, Y g:i A'); // Build HTML content with simplified styling $html = ' <style> body { font-family: Arial, sans-serif; margin: 15px; } h1 { text-align: center; color: #333; margin-bottom: 5px; } h2 { text-align: center; color: #555; margin-top: 0; } table { width: 100%; border-collapse: collapse; margin-top: 15px; } th { background-color: #D1B48C; color: #000; padding: 8px; text-align: left; } td { padding: 8px; border-bottom: 1px solid #ddd; } .text-right { text-align: right; } .total-row { font-weight: bold; background-color: #f5f5f5; } </style> <h1>ArKi Inventory</h1> <h2>Inventory Report</h2> <p style="text-align: center;">Exported on: ' . $export_date . '</p> <table> <thead> <tr> <th>Product Name</th> <th>Category</th> <th>Unit</th> <th class="text-right">Cost Price</th> <th class="text-right">Retail Price</th> <th class="text-right">Stock</th> <th>Date Added</th> <th>Date Updated</th> <th class="text-right">Stock Value</th> </tr> </thead> <tbody>'; $total_stock_value = 0; foreach ($inventory_data as $item) { $stock_value = $item['in_stock'] * $item['buying_price']; $total_stock_value += $stock_value; $html .= ' <tr> <td>' . htmlspecialchars($item['name']) . '</td> <td>' . htmlspecialchars($item['category']) . '</td> <td>' . htmlspecialchars($item['unit']) . '</td> <td class="text-right">&#8369;' . number_format($item['buying_price'], 2) . '</td> <td class="text-right">&#8369;' . number_format($item['selling_price'], 2) . '</td> <td class="text-right">' . htmlspecialchars($item['in_stock']) . '</td> <td>' . htmlspecialchars(date('M j, Y', strtotime($item['created_at']))) . '</td> <td>' . htmlspecialchars(date('M j, Y', strtotime($item['updated_at']))) . '</td> <td class="text-right">&#8369;' . number_format($stock_value, 2) . '</td> </tr>'; } $html .= ' <tr class="total-row"> <td colspan="8" class="text-right">Total Stock Value (Cost):</td> <td class="text-right">&#8369;' . number_format($total_stock_value, 2) . '</td> </tr> </tbody> </table>'; // Load HTML to dompdf $dompdf->loadHtml($html); // Set paper size and orientation $dompdf->setPaper('A4', 'landscape'); // Render the HTML as PDF $dompdf->render(); // Generate a filename $filename = 'ArKi-Inventory-Report_' . date('Y-m-d') . '.pdf'; // Output the PDF with proper headers header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Transfer-Encoding: binary'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); echo $dompdf->output(); exit; } catch (Exception $e) { die('Error generating PDF: ' . $e->getMessage()); } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Inventory Report</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.13/jspdf.plugin.autotable.min.js"></script> <!-- Initialize jsPDF --> <script> window.jsPDF = window.jspdf.jsPDF; </script> <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> /* Base & Layout Styles - Flexbox Fix for Sidebar Overlap - VERSION 4 - Flex Basis and Max-Width */ 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; flex-basis: 240px; /* Use flex-basis instead of width */ /* ADDED */ /* Prevent sidebar from becoming wider */ /* ADDED */ 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; flex-shrink: 0; /* Prevent sidebar from shrinking */ } .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; } .header { position: fixed; top: 0; left: 240px; right: 0; display: flex; justify-content: space-between; align-items: center; background-color: #77693A; padding: 10px 30px; height: 60px; /* Fixed height */ z-index: 999; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .header-left .date-time { font-size: 15px; /* Slightly smaller */ color: #fff; /* 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: #ffffff; } /* 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 */ } .user-dropdown { position: relative; display: inline-block; cursor: pointer; } .user-dropdown-content { display: none; position: absolute; right: 0; background-color: #D1B48C; min-width: 100px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; border-radius: 4px; } .user-dropdown-header { padding: 8px 12px; background-color: ; #D1B48C border-bottom: 1px solid #e9ecef; display: flex; align-items: center; gap: 8px; } .user-dropdown-header i { font-size: 20px; color: #000000; } .user-dropdown-content.user-info { display: flex; align-items: right; gap: 1px; color: #000000; } .user-dropdown-content a { display: flex; padding: 8px 15px; align-items: center; color: #000000; text-decoration: none; font-size: 16px; transition: all 0.2s; } .user-dropdown-content a i { font-size: 16px; /* Adjust logout icon size separately */ margin-right: 8px; /* Space between icon and text */ } .user-dropdown-content a:hover { text-decoration: underline; color: #000000; } .user-dropdown:hover .user-dropdown-content { display: block; } .user-dropdown-content.user-info i { font-size: 16px; align-items: center; } .header-right i { color: #ffffff; /* Icon color */ font-size:40px; /* Larger icon */ /* Remove margin-right when stacked */ } .header-right span { font-size: 15px; /* Smaller font for username */ color: #ffffff; } .main-content { margin-left: 240px; padding: 80px 20px 20px 20px; flex-grow: 1; } .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; } .product-filters-form { display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px; padding: 15px; background-color: #f8f9fa; border: 1px solid #eee; border-radius: 6px; } .filter-group { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; } .product-filters-form label { font-weight: 600; font-size: 0.95em; margin-bottom: 0; white-space: nowrap; } .product-filters-form input[type="text"], .product-filters-form input[type="date"] { padding: 8px 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 0.95em; font-family: inherit; box-sizing: border-box; flex-grow: 1; min-width: 140px; } .search-input-wrapper { position: relative; display: flex; flex-grow: 1; align-items: center; } #product-search { padding-right: 30px; } .product-search-clear { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); background: none; border: none; font-size: 1.5em; color: #aaa; cursor: pointer; padding: 0 4px; line-height: 1; display: <?php echo !empty($search_term) ? 'inline-block' : 'none'; ?>; transition: color 0.2s ease-in-out; } .product-search-clear:hover { color: #555; } .product-filters-form button[type="submit"] { padding: 8px 15px; background-color: #D1B48C; color: black; border: none; border-radius: 4px; cursor: pointer; font-size: 1em; line-height: 1.5; transition: background-color 0.3s ease; flex-shrink: 0; } .product-filters-form button[type="submit"]:hover { background-color: #be9b7b ; } .inventory-container { background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); border: 1px solid #eee; overflow-x: auto; } .report-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; flex-wrap: wrap; gap: 10px;} .report-header h2 { margin: 0; color: #2f4050; font-size: 20px; } #inventory-table { width: 100%; border-collapse: collapse; margin-top: 10px; table-layout: fixed; } #inventory-table th, #inventory-table td { padding: 10px 12px; text-align: center; border-bottom: 1px solid #eee; font-size: 0.95em; vertical-align: middle; word-wrap: break-word; } #inventory-table th { background-color: #f9f9f9; font-weight: 600; color: #555; white-space: nowrap; } #inventory-table tbody tr:hover { background-color: #f5f5f5; } #inventory-table th.numeric, #inventory-table td.numeric { text-align: center; } #inventory-table td.low-stock { background-color: #f8d7da; color: #721c24; } #inventory-table tfoot td { font-weight: bold; font-size: 1.1em; border-top: 2px solid #333; padding-top: 15px; padding-bottom: 15px; } #inventory-table tfoot td.total-label { text-align: right; padding-right: 10px; } #inventory-table tfoot td.total-value { text-align: right; } .delete-button { color: black; padding: 5px; text-decoration: none; display: inline-block; transition: color 0.2s; } .clear-all-button { margin-bottom: 1px; /* Adds space below the button */ margin-left: auto; /* Ensures the button pushes itself to the right */ display: block; /* Ensures the button behaves as a block element */ font-weight: 500; padding: 8px 15px; background-color: #D1B48C; color: #0000000; text-decoration: none; border-radius: 4px; font-size: 16px; transition: background-color 0.3s; line-height: 0.5; font-weight: 500; } } </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; ?> </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"> <!-- Updated user info with dropdown --> <div class="user-dropdown"> <div class="user-info"> <span id="current-username-header"> <?php echo esc_html($current_username); ?> <i class="fas fa-user-circle"></i> </span> </div> <div class="user-dropdown-content"> <div class="user-dropdown-header"> <i class="fas fa-user-circle"style="color:#000000;"></i> <span style="color:#000000;"><?php echo esc_html($current_username); ?></span> </div> <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" style="font-size: 16px; color:#000000;"></i>Logout </a> </div> </div> </div> </header> <h1>Inventory Report</h1> <hr/> </header> <?php if ($message): ?> <div class="notice-wrap"> <div class="notice notice-<?php echo esc_attr($message_type); ?>"> <p><?php echo esc_html($message); ?></p> </div> </div> <?php endif; ?> <?php if (!empty($fetch_error)): ?> <div class="notice-wrap"> <div class="notice notice-error"> <p><?php echo esc_html($fetch_error); ?></p> </div> </div> <?php endif; ?> <div class="inventory-container"> <!-- Filter Form --> <form method="GET" class="product-filters-form"> <div class="filter-group search-input-wrapper"> <label for="product-search">Search:</label> <input type="text" id="product-search" name="product_search" placeholder="Name or Category..." value="<?php echo esc_attr($search_term); ?>"> <button type="button" class="product-search-clear" id="clear-search-btn" title="Clear search">&times;</button> </div> <div class="filter-group"> <label for="start-date">Date Added From:</label> <input type="date" id="start-date" name="start_date" value="<?php echo esc_attr($start_date); ?>"> </div> <div class="filter-group"> <label for="end-date">Date Added To:</label> <input type="date" id="end-date" name="end_date" value="<?php echo esc_attr($end_date); ?>"> </div> <button type="submit"><i class="fas fa-filter"></i> Filter</button> </form> <!-- End Filter Form --> <div id="inventory-report-content"> <div class="report-header"> <h2>Current Inventory Status</h2> <?php if ($current_username === 'admin'): ?> <a href="?export_pdf=1" style= " display: inline-block; padding: 8px 15px;background-color: #d9534f;color: #0000000;text-decoration: none;border-radius: 4px;margin-right: 10px;font-size: 16px;transition: background-color 0.3s;line-height: 1;display: flex;gap: 10px;font-weight: 500;"> Export as PDF </a> <a href="<?php echo esc_url(wp_nonce_url(add_query_arg(['clear_all_products' => 'true']), 'clear_all_products_action')); ?>" class="clear-all-button" onclick="return confirm('WARNING: This will delete ALL products. Are you sure?')"> <i class="fas fa-trash"></i> Clear All </a> </div> <?php endif; ?> </div> <table id="inventory-table"> <thead> <tr> <th>Product Name</th> <th>Category</th> <th>Unit</th> <th class="numeric">Cost Price</th> <th class="numeric">Retail Price</th> <th class="numeric">Current Stock</th> <th>Date Added</th> <th>Last Updated</th> <th class="numeric">Stock Value (Cost)</th> <th>Action</th> </tr> </thead> <tbody> <?php if (empty($inventory_data) && empty($fetch_error)): ?> <tr> <td colspan="8" style="text-align: center; color: #666; padding: 20px;"> <?php echo (!empty($search_term) || !empty($start_date) || !empty($end_date)) ? 'No products match your filters.' : 'No products found in inventory.'; ?> </td> </tr> <?php elseif (!empty($inventory_data)): ?> <?php $low_stock_threshold = 5; // Define your low stock threshold here foreach ($inventory_data as $item): $stock_value = $item['in_stock'] * $item['buying_price']; $is_low_stock = $item['in_stock'] <= $low_stock_threshold && $item['in_stock'] > 0; ?> <tr class="<?php echo $is_low_stock ? 'low-stock' : ''; ?>"> <td><?php echo esc_html($item['name']); ?></td> <td><?php echo esc_html($item['category']); ?></td> <td><?php echo esc_html($item['unit']); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($item['buying_price'], 2); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($item['selling_price'], 2); ?></td> <td class="numeric"><?php echo esc_html($item['in_stock']); ?></td> <td><?php echo esc_html(mysql2date(get_option('date_format'), $item['created_at'])); ?></td> <td><?php echo esc_html(mysql2date(get_option('date_format'), $item['updated_at'])); ?></td> <td class="numeric"><?php echo esc_html($currency_symbol); ?><?php echo number_format($stock_value, 2); ?></td> <td> <a href="<?php echo esc_url(wp_nonce_url(add_query_arg(['delete_product' => 'true', 'id' => $item['id']]), 'delete_product_' . $item['id'])); ?>" class="delete-button" onclick="return confirm('Are you sure you want to delete this product?')"> <i class="fas fa-trash"></i>Delete </a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> <tfoot> <tr> <td colspan="7" class="total-label">Total Stock Value (at Cost):</td> <td class="numeric total-value"><?php echo esc_html($currency_symbol); ?><?php echo number_format($total_stock_value, 2); ?></td> </tr> </tfoot> </table> </div> <!-- End #inventory-report-content --> </div> <!-- End .inventory-container --> </main> </div> <script> document.addEventListener('DOMContentLoaded', function() { console.log("Inventory Report Script: DOM Loaded"); // --- Clear Search Button Logic (No changes) --- const searchInput = document.getElementById('product-search'); const clearButton = document.getElementById('clear-search-btn'); if (searchInput && clearButton) { const toggleClearButton = () => { clearButton.style.display = searchInput.value.trim() !== '' ? 'inline-block' : 'none'; }; searchInput.addEventListener('input', toggleClearButton); clearButton.addEventListener('click', function(event) { event.preventDefault(); searchInput.value = ''; clearButton.style.display = 'none'; const filterForm = searchInput.closest('form'); if (filterForm) { filterForm.submit(); } else { console.error("Clear Button Script: Could not find parent form."); } }); toggleClearButton(); // Initial check console.log("Clear Button Script: Setup Complete."); } else { if (!searchInput) console.warn("Clear Button Script: Could not find search input #product-search."); if (!clearButton) console.warn("Clear Button Script: Could not find clear button #clear-search-btn."); } // --- End Clear Search Button Logic --- </script> <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> <?php // End of PHP Snippet ?>
wheres the code?
i cant open the downloaded pdf file of my sales report. // Handle PDF export if (isset($_GET['export_pdf'])) { // Clear any previous output if (ob_get_level()) { ob_end_clean(); // Clean any previous output } // Path to dompdf $dompdf_path = ABSPATH . 'wp-content/plugins/dompdf/autoload.inc.php'; if (!file_exists($dompdf_path)) { die('Dompdf library not found at: ' . $dompdf_path); } require_once($dompdf_path); try { // Create PDF document with options $options = new \Dompdf\Options(); $options->set('isRemoteEnabled', true); // Allow remote content $options->set('isHtml5ParserEnabled', true); // Use HTML5 parser $options->set('isPhpEnabled', false); // Disable PHP for security $options->set('defaultFont', 'Arial'); // Set default font $dompdf = new \Dompdf\Dompdf($options); // Get the current date in the specific timezone $timezone = new DateTimeZone('Asia/Manila'); $export_date = (new DateTime('now', $timezone))->format('M j, Y g:i A'); // Build HTML content for the PDF $html = ' <style> body { font-family: Arial, sans-serif; margin: 15px; } h1, h2 { text-align: center; } h1 { color: #333; margin-bottom: 1px; } h2 { color: #555; margin-top: 0; } table { width: 100%; border-collapse: collapse; margin-top: 15px; } th, td { padding: 8px; border: 1px solid #ddd; } th { background-color: #D1B48C; color: #000; } .total-row { font-weight: bold; background-color: #f5f5f5; } </style> <h1>ArKi Inventory</h1> <h2>Sales Report</h2> <p style="text-align: center;">Exported on: ' . $export_date . '</p> <table> <thead> <tr> <th>Product Name</th> <th>Date Sold</th> <th>Category</th> <th>Unit</th> <th>Unit Price</th> <th>Quantity Sold</th> <th>Product Revenue</th> <th>Product Profit</th> </tr> </thead> <tbody>'; foreach ($sales_data as $sale) { $html .= ' <tr> <td>' . htmlspecialchars($sale['product_name']) . '</td> <td>' . htmlspecialchars($sale['date_sold']) . '</td> <td>' . htmlspecialchars($sale['category']) . '</td> <td>' . htmlspecialchars($sale['unit']) . '</td> <td>' . htmlspecialchars($currency_symbol) . number_format($sale['unit_price'], 2) . '</td> <td>' . htmlspecialchars($sale['quantity_sold']) . '</td> <td>' . htmlspecialchars($currency_symbol) . number_format($sale['product_revenue'], 2) . '</td> <td>' . htmlspecialchars($currency_symbol) . number_format($sale['product_profit'], 2) . '</td> </tr>'; } $html .= ' <tr class="total-row"> <th colspan="6" style="text-align:right;">Total Product Revenue:</th> <th>' . htmlspecialchars($currency_symbol) . number_format($total_revenue, 2) . '</th> <th>' . htmlspecialchars($currency_symbol) . number_format($total_profit, 2) . '</th> </tr> </tbody> </table>'; // Load HTML content $dompdf->loadHtml($html); $dompdf->setPaper('A4', 'portrait'); // Set paper size and orientation $dompdf->render(); // Render PDF // Generate a relevant filename $filename = 'ArKi-Sales-Report_' . date('Y-m-d') . '.pdf'; // Output the PDF with proper headers header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Transfer-Encoding: binary'); header('Cache-Control: must-revalidate'); header('Pragma: public'); // Output the generated PDF echo $dompdf->output(); exit; } catch (Exception $e) { die('Error generating PDF: ' . htmlspecialchars($e->getMessage())); } }

Here's the updated code with the PDF export functionality fixed:

// Handle PDF export
if (isset($_GET['export_pdf'])) {
    // Clear any previous output
    if (ob_get_level()) {
        ob_end_clean();
    }
    
    // Check if dompdf is properly installed
    $dompdf_path = ABSPATH . 'wp-content/plugins/dompdf/autoload.inc.php';
    if (!file_exists($dompdf_path)) {
        die('Dompdf library not found. Please ensure dompdf is installed in: ' . $dompdf_path);
    }
    
    require_once($dompdf_path);
    
    try {
        // Create PDF document with improved settings
        $options = new \Dompdf\Options();
        $options->set('isRemoteEnabled', true);
        $options->set('isHtml5ParserEnabled', true);
        $options->set('isPhpEnabled', false); // Disable PHP for security
        $options->set('defaultFont', 'helvetica');
        $options->set('isFontSubsettingEnabled', true);
        
        $dompdf = new \Dompdf\Dompdf($options);
        
        // Get current date in Philippines timezone
        $timezone = new DateTimeZone('Asia/Manila');
        $export_date = (new DateTime('now', $timezone))->format('M j, Y g:i A');
        
        // Build HTML content with simplified styling
        $html = '
        <style>
            body { font-family: Arial, sans-serif; margin: 15px; }
            h1 { text-align: center; color: #333; margin-bottom: 5px; }
            h2 { text-align: center; color: #555; margin-top: 0; }
            table { width: 100%; border-collapse: collapse; margin-top: 15px; }
            th { background-color: #D1B48C; color: #000; padding: 8px; text-align: left; }
            td { padding: 8px; border-bottom: 1px solid #ddd; }
            .text-right { text-align: right; }
            .total-row { font-weight: bold; background-color: #f5f5f5; }
        </style>
        <h1>ArKi Inventory</h1>
        <h2>Sales Report</h2>
        <p style="text-align: center;">Exported on: ' . $export_date . '</p>
        <table>
            <thead>
                <tr>
                    <th>Product Name</th>
                    <th>Date Sold</th>
                    <th>Category</th>
                    <th>Unit</th>
                    <th class="text-right">Unit Price</th>
                    <th class="text-right">Qty Sold</th>
                    <th class="text-right">Product Revenue</th>
                    <th class="text-right">Product Profit</th>
                </tr>
            </thead>
            <tbody>';
        
        foreach ($sales_data as $sale) {
            $html .= '
                <tr>
                    <td>' . htmlspecialchars($sale['product_name']) . '</td>
                    <td>' . htmlspecialchars($sale['date_sold']) . '</td>
                    <td>' . htmlspecialchars($sale['category']) . '</td>
                    <td>' . htmlspecialchars($sale['unit']) . '</td>
                    <td class="text-right">' . htmlspecialchars($currency_symbol) . number_format($sale['unit_price'], 2) . '</td>
                    <td class="text-right">' . htmlspecialchars($sale['quantity_sold']) . '</td>
                    <td class="text-right">' . htmlspecialchars($currency_symbol) . number_format($sale['product_revenue'], 2) . '</td>
                    <td class="text-right">' . htmlspecialchars($currency_symbol) . number_format($sale['product_profit'], 2) . '</td>
                </tr>';
        }
        
        $html .= '
                <tr class="total-row">
                    <td colspan="6" class="text-right">Total Product Revenue:</td>
                    <td class="text-right">' . htmlspecialchars($currency_symbol) . number_format($total_revenue, 2) . '</td>
                    <td class="text-right">' . htmlspecialchars($currency_symbol) . number_format($total_profit, 2) . '</td>
                </tr>
            </tbody>
        </table>';
        
        // Load HTML to dompdf
        $dompdf->loadHtml($html);
        
        // Set paper size and orientation
        $dompdf->setPaper('A4', 'landscape');
        
        // Render the HTML as PDF
        $dompdf->render();
        
        // Generate a filename
        $filename = 'ArKi-Sales-Report_' . date('Y-m-d') . '.pdf';
        
        // Output the PDF with proper headers
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Transfer-Encoding: binary');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        
        // Force download
        $dompdf->stream($filename, ['Attachment' => 1]);
        exit;
        
    } catch (Exception $e) {
        die('Error generating PDF: ' . $e->getMessage());
    }
}

Key improvements made:

  1. Changed the output method from echo $dompdf->output() to $dompdf->stream() which is more reliable for downloads
  2. Added proper error checking for dompdf installation
  3. Improved PDF styling and layout (landscape orientation for better readability)
  4. Added proper content-type headers
  5. Disabled PHP execution in the PDF for security
  6. Added font subsetting to reduce file size
  7. Improved error handling and reporting

Also, make sure you have the dompdf library properly installed in your WordPress plugins directory (wp-content/plugins/dompdf/). If you're still having issues, check:

  1. The dompdf folder exists in your plugins directory
  2. The folder has proper read permissions
  3. Your server has enough memory to generate the PDF (especially for large reports)
  4. There are no PHP errors being output before the PDF generation starts

The PDF should now download properly when clicking the "Export as PDF" button.

i tried to open the downloaded file but it says "The file can't be open". I used Microsoft Edge to open it. Make the downloaded file to be available and can be opened by all PDF viewer

Here's the updated PDF export code that ensures the generated PDF is compatible with all PDF viewers:

// Handle PDF export
if (isset($_GET['export_pdf'])) {
    // Clear any previous output
    while (ob_get_level()) {
        ob_end_clean();
    }

    // Check if dompdf is properly installed
    $dompdf_path = ABSPATH . 'wp-content/plugins/dompdf/autoload.inc.php';
    if (!file_exists($dompdf_path)) {
        die('Dompdf library not found. Please ensure dompdf is installed in: ' . $dompdf_path);
    }
    
    require_once($dompdf_path);
    
    try {
        // Create PDF document with improved settings for compatibility
        $options = new \Dompdf\Options();
        $options->set('isRemoteEnabled', true);
        $options->set('isHtml5ParserEnabled', true);
        $options->set('isPhpEnabled', false);
        $options->set('defaultFont', 'helvetica');
        $options->set('isFontSubsettingEnabled', true);
        $options->set('isJavascriptEnabled', false);
        $options->set('debugKeepTemp', false);
        $options->set('debugCss', false);
        $options->set('debugLayout', false);
        
        $dompdf = new \Dompdf\Dompdf($options);
        
        // Get current date in Philippines timezone
        $timezone = new DateTimeZone('Asia/Manila');
        $export_date = (new DateTime('now', $timezone))->format('M j, Y g:i A');
        
        // Build HTML content with basic styling for maximum compatibility
        $html = '
        <!DOCTYPE html>
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <style>
                body { font-family: Arial, sans-serif; margin: 15px; }
                h1 { text-align: center; color: #333; margin-bottom: 5px; }
                h2 { text-align: center; color: #555; margin-top: 0; }
                table { width: 100%; border-collapse: collapse; margin-top: 15px; }
                th { background-color: #D1B48C; color: #000; padding: 8px; text-align: left; }
                td { padding: 8px; border-bottom: 1px solid #ddd; }
                .text-right { text-align: right; }
                .total-row { font-weight: bold; background-color: #f5f5f5; }
            </style>
        </head>
        <body>
            <h1>ArKi Inventory</h1>
            <h2>Sales Report</h2>
            <p style="text-align: center;">Exported on: ' . $export_date . '</p>
            <table>
                <thead>
                    <tr>
                        <th>Product Name</th>
                        <th>Date Sold</th>
                        <th>Category</th>
                        <th>Unit</th>
                        <th class="text-right">Unit Price</th>
                        <th class="text-right">Qty Sold</th>
                        <th class="text-right">Product Revenue</th>
                        <th class="text-right">Product Profit</th>
                    </tr>
                </thead>
                <tbody>';
        
        foreach ($sales_data as $sale) {
            $html .= '
                    <tr>
                        <td>' . htmlspecialchars($sale['product_name']) . '</td>
                        <td>' . htmlspecialchars($sale['date_sold']) . '</td>
                        <td>' . htmlspecialchars($sale['category']) . '</td>
                        <td>' . htmlspecialchars($sale['unit']) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($sale['unit_price'], 2) . '</td>
                        <td class="text-right">' . htmlspecialchars($sale['quantity_sold']) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($sale['product_revenue'], 2) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($sale['product_profit'], 2) . '</td>
                    </tr>';
        }
        
        $html .= '
                    <tr class="total-row">
                        <td colspan="6" class="text-right">Total Product Revenue:</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($total_revenue, 2) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($total_profit, 2) . '</td>
                    </tr>
                </tbody>
            </table>
        </body>
        </html>';
        
        // Load HTML to dompdf
        $dompdf->loadHtml($html, 'UTF-8');
        
        // Set paper size and orientation
        $dompdf->setPaper('A4', 'landscape');
        
        // Render the HTML as PDF
        $dompdf->render();
        
        // Generate a filename
        $filename = 'ArKi-Sales-Report_' . date('Y-m-d') . '.pdf';
        
        // Output the PDF with proper headers
        header('Content-Description: File Transfer');
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        header('Content-Length: ' . strlen($dompdf->output()));
        
        // Output the PDF and force download
        echo $dompdf->output();
        exit;
        
    } catch (Exception $e) {
        die('Error generating PDF: ' . $e->getMessage());
    }
}

Key improvements for compatibility:

  1. Added proper DOCTYPE and HTML structure
  2. Explicitly set UTF-8 encoding in both meta tag and loadHtml()
  3. Simplified CSS to use only basic styling
  4. Added space between currency symbol and amount for better readability
  5. Added more comprehensive HTTP headers
  6. Disabled JavaScript in PDF generation
  7. Added Content-Length header
  8. Ensured all output buffering is cleared before generating PDF
  9. Used basic font (Arial) that's available on most systems
  10. Added Content-Description header

Also, make sure to:

  1. Verify dompdf is installed correctly in wp-content/plugins/dompdf/
  2. Check your server error logs for any PHP errors during PDF generation
  3. Ensure your server has enough memory (at least 128MB recommended)
  4. Test with a small set of data first to isolate any issues

The PDF should now be properly formatted and compatible with all PDF viewers including Microsoft Edge, Adobe Reader, and others.

it show all this error when i click "Export PDF" Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 254 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 256 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 257 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 254 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 256 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 257 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 254 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 256 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 257 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 254 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 256 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 257 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 254 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 256 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 257 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 264 Warning: Undefined variable $currency_symbol in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 265 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 285 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 286 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 287 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 288 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 289 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 290 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 291 Warning: Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code:254) in C:\xampp\htdocs\inventory\wp-content\plugins\insert-php-code-snippet\shortcode-handler.php(238) : eval()'d code on line 292 %PDF-1.7 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >> endobj 2 0 obj << /Type /Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Kids [6 0 R ] /Count 1 /Resources << /ProcSet 4 0 R /Font << /F1 8 0 R /F2 9 0 R >> >> /MediaBox [0.000 0.000 841.890 595.280] >> endobj 4 0 obj [/PDF /Text ] endobj 5 0 obj << /Producer (��dompdf 3.1.0 + CPDF) /CreationDate (D:20250508152141+00'00') /ModDate (D:20250508152141+00'00') >> endobj 6 0 obj << /Type /Page /MediaBox [0.000 0.000 841.890 595.280] /Parent 3 0 R /Contents 7 0 R >> endobj 7 0 obj << /Filter /FlateDecode /Length 1414 >> stream x���Ks�6���讝iQ�޵n�6�;n�2Yh$&��"3������2q�@����=�8@���1���?����H�0�hΨ��mɏ��2r���w�����'�_��0{O�^��w/�R��w4�z�r� � w(�Z�w�m�e�9vtg9p���҂ 5�� �{��$�m�8\���q���&�_ Nno�NL����O=���"���*��ũd��;��M�c�3�Hi�v�����s��]婑��*�.�!�>ֶ}��>td5�o�MH�)� M8M��y��K�����<@��P�z�тr�8u��)!Si���C8���Tj��aB�0��AH�½�j�ϵ?V�Q�FA伴����3���B�8a�w�� ;K�6 ��:�1�%U��_喝p� ��bK3<�z6�P+��lm�g��B�U2�!�s��a�ƹ�g�j�/�1~(�����E����RkM^�{�*W���`�m<�����l�æ"-��u�6m�v�j�T�j#ᯬT'�r�}��]����Vk�=^���!- [D{���sF9հ�+$�˲�1��w�Dd�[(� �Ut�0ra�B�K+ �ƒ����p����k�����B G �#x*�W��* �x��������龁熇�z��b:��?���݀��;��P�% l���4�p�+Y 䴞�6eI K ;��Rn #؜�0�<��4O��l���<� �n��,ե̎�6� c���(ZJ�n��h �B�� îm�I����pF^�cz��2�c���!����6/#��a�lr�q������!�������f=<��P�SLt�4PѶ�=O�+njP��ՠBSHAn*@%��N�H?Ulx��� �@��HC��3TmY��P�0� ����*E �����U��p�*P��}*G�;�p�� ֎��az朅J��9����`� }�<>������U���*HES���"�����2RI����"�lPC*��EH5e�HE��C!�H��O�����R4 u��R&"U#RMaYC N5nt��|�@�=�8U]��_�=Y���j|����MGn��ͧS0��2����rdjO`� E����kSP�=a�sO�Es ��a���p�Dq�ő��8gl�� �8��X�R���\�h �S*P�\�*ڢ�JET��HESX�oA�,���8�m3��6��C'h���k}��3�yBS�2�AG-KO�pe��,���ReerW�UVƈ�ZeerW�UV�DgC,*��R��2�8�**����2�6�*+����GC �qXy�u�;��xXߓ�%w�Xt����9W�G�4V�P"?��ʡ$w]08�Svap\���Ďσ�?}��)�Gf���֓�.�5���0�~��w1eB endstream endobj 8 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding >> endobj xref 0 10 0000000000 65535 f 0000000009 00000 n 0000000074 00000 n 0000000120 00000 n 0000000284 00000 n 0000000313 00000 n 0000000462 00000 n 0000000565 00000 n 0000002052 00000 n 0000002159 00000 n trailer << /Size 10 /Root 1 0 R /Info 5 0 R /ID[<6ab9e2cb107dfcd50118189902f6a53f><6ab9e2cb107dfcd50118189902f6a53f>] >> startxref 2271 %%EOF

The errors indicate two main issues: undefined $currency_symbol variable and headers already sent. Here's the fixed version that will work properly:

// Handle PDF export
if (isset($_GET['export_pdf'])) {
    // Clear any previous output
    while (ob_get_level()) {
        ob_end_clean();
    }

    // Check if dompdf is properly installed
    $dompdf_path = ABSPATH . 'wp-content/plugins/dompdf/autoload.inc.php';
    if (!file_exists($dompdf_path)) {
        die('Dompdf library not found. Please ensure dompdf is installed in: ' . $dompdf_path);
    }
    
    require_once($dompdf_path);
    
    try {
        // Get currency symbol (define if not already set)
        if (!isset($currency_symbol)) {
            $currency_symbol = function_exists('get_woocommerce_currency_symbol') ? 
                get_woocommerce_currency_symbol() : '₱';
        }
        
        // Create PDF document with improved settings for compatibility
        $options = new \Dompdf\Options();
        $options->set('isRemoteEnabled', true);
        $options->set('isHtml5ParserEnabled', true);
        $options->set('isPhpEnabled', false);
        $options->set('defaultFont', 'helvetica');
        $options->set('isFontSubsettingEnabled', true);
        $options->set('isJavascriptEnabled', false);
        $options->set('debugKeepTemp', false);
        $options->set('debugCss', false);
        $options->set('debugLayout', false);
        
        $dompdf = new \Dompdf\Dompdf($options);
        
        // Get current date in Philippines timezone
        $timezone = new DateTimeZone('Asia/Manila');
        $export_date = (new DateTime('now', $timezone))->format('M j, Y g:i A');
        
        // Build HTML content with basic styling for maximum compatibility
        $html = '
        <!DOCTYPE html>
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <style>
                body { font-family: Arial, sans-serif; margin: 15px; }
                h1 { text-align: center; color: #333; margin-bottom: 5px; }
                h2 { text-align: center; color: #555; margin-top: 0; }
                table { width: 100%; border-collapse: collapse; margin-top: 15px; }
                th { background-color: #D1B48C; color: #000; padding: 8px; text-align: left; }
                td { padding: 8px; border-bottom: 1px solid #ddd; }
                .text-right { text-align: right; }
                .total-row { font-weight: bold; background-color: #f5f5f5; }
            </style>
        </head>
        <body>
            <h1>ArKi Inventory</h1>
            <h2>Sales Report</h2>
            <p style="text-align: center;">Exported on: ' . $export_date . '</p>
            <table>
                <thead>
                    <tr>
                        <th>Product Name</th>
                        <th>Date Sold</th>
                        <th>Category</th>
                        <th>Unit</th>
                        <th class="text-right">Unit Price</th>
                        <th class="text-right">Qty Sold</th>
                        <th class="text-right">Product Revenue</th>
                        <th class="text-right">Product Profit</th>
                    </tr>
                </thead>
                <tbody>';
        
        foreach ($sales_data as $sale) {
            $html .= '
                    <tr>
                        <td>' . htmlspecialchars($sale['product_name']) . '</td>
                        <td>' . htmlspecialchars($sale['date_sold']) . '</td>
                        <td>' . htmlspecialchars($sale['category']) . '</td>
                        <td>' . htmlspecialchars($sale['unit']) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($sale['unit_price'], 2) . '</td>
                        <td class="text-right">' . htmlspecialchars($sale['quantity_sold']) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($sale['product_revenue'], 2) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($sale['product_profit'], 2) . '</td>
                    </tr>';
        }
        
        $html .= '
                    <tr class="total-row">
                        <td colspan="6" class="text-right">Total Product Revenue:</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($total_revenue, 2) . '</td>
                        <td class="text-right">' . htmlspecialchars($currency_symbol) . ' ' . number_format($total_profit, 2) . '</td>
                    </tr>
                </tbody>
            </table>
        </body>
        </html>';
        
        // Load HTML to dompdf
        $dompdf->loadHtml($html, 'UTF-8');
        
        // Set paper size and orientation
        $dompdf->setPaper('A4', 'landscape');
        
        // Render the HTML as PDF
        $dompdf->render();
        
        // Generate a filename
        $filename = 'ArKi-Sales-Report_' . date('Y-m-d') . '.pdf';
        
        // Output the PDF with proper headers
        header('Content-Description: File Transfer');
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Transfer-Encoding: binary');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Pragma: public');
        
        // Output the PDF and force download
        echo $dompdf->output();
        exit;
        
    } catch (Exception $e) {
        // Ensure no output has been sent before showing error
        while (ob_get_level()) {
            ob_end_clean();
        }
        die('Error generating PDF: ' . $e->getMessage());
    }
}

Key fixes made:

  1. Added check for $currency_symbol variable and defined it if not set
  2. Improved output buffering with while (ob_get_level()) to ensure all buffers are cleared
  3. Added proper error handling that also clears output buffers
  4. Ensured all headers are sent before any content
  5. Added UTF-8 encoding declaration in the HTML
  6. Used simple, compatible font (Helvetica)
  7. Added proper spacing between currency symbol and amounts

This should now:

  • Properly handle the currency symbol
  • Not produce any "headers already sent" warnings
  • Generate a valid PDF file that can be opened in any PDF viewer
  • Work consistently in your WordPress environment

Make sure you have the dompdf library properly installed in your WordPress plugins directory (wp-content/plugins/dompdf/). If you're still having issues, you might want to check your PHP error logs for any other underlying problems._

the peso sign is not showing, instead it shows "?"