0, 'available' => 0, 'draft' => 0, 'sent' => 0, 'accepted' => 0, 'declined' => 0, 'cancelled' => 0, 'expired' => 0 ]; // Get current view and status filter $view = isset($_GET['view']) ? sanitize_text_field($_GET['view']) : 'all'; $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : ''; // Define status filters $status_filters = array( 'available' => 'Available', 'draft' => 'Draft', 'sent' => 'Sent', 'accepted' => 'Accepted', 'declined' => 'Declined', 'cancelled' => 'Trash', 'expired' => 'Expired' ); // Extract quote data for statistics $formatted_quotes = array(); foreach ($quotes as $quote) { // For Quote model objects if (is_object($quote)) { $formatted_quotes[] = array( 'id' => $quote->getId(), 'amount' => $quote->getTotal(), 'status' => $quote->getStatus(), ); } else { // For already formatted quote arrays $formatted_quotes[] = array( 'id' => $quote['id'], 'amount' => isset($quote['total_amount']) ? $quote['total_amount'] : $quote['amount'], 'status' => $quote['status'], ); } } // Group quotes by currency for proper total calculation $currency_totals = []; $total_quotes = count($formatted_quotes); foreach ($quotes as $quote) { if (is_object($quote)) { $currency_code = $quote->getCurrencyCode() ?: 'USD'; $amount = $quote->getTotal() ?: 0; if (!isset($currency_totals[$currency_code])) { $currency_totals[$currency_code] = [ 'total' => 0, 'quotes' => 0, 'quote_object' => $quote // Keep reference for formatting ]; } $currency_totals[$currency_code]['total'] += floatval($amount); $currency_totals[$currency_code]['quotes']++; } } $enhanced_stats = [ 'total_quotes' => $total_quotes, 'currency_totals' => $currency_totals, 'accepted_quotes' => count(array_filter($formatted_quotes, function($quote) { return $quote['status'] === 'accepted'; })), 'pending_quotes' => count(array_filter($formatted_quotes, function($quote) { return $quote['status'] === 'sent' || $quote['status'] === 'draft'; })), 'rejected_quotes' => count(array_filter($formatted_quotes, function($quote) { return $quote['status'] === 'rejected'; })) ]; ?>

Total Quotes

Total Value

$0.00

$currency_data): ?> format($currency_data['total']); ?>
quote 1 ? 's' : ''; ?>

Accepted Quotes

Pending Quotes

1): ?>
Showing to of results
Page of
1 ? $pagination['current_page'] - 1 : 1; $next_page = $pagination['current_page'] < $pagination['total_pages'] ? $pagination['current_page'] + 1 : $pagination['total_pages']; $current_url = add_query_arg($_GET, admin_url('admin.php')); $current_url = remove_query_arg('paged', $current_url); $start_page = max(1, $pagination['current_page'] - 2); $end_page = min($pagination['total_pages'], $pagination['current_page'] + 2); if ($pagination['current_page'] > 1): ?> ‹ Previous Next ›
registerPostTypes(); $easy_invoice->forceFlushRewriteRules(); // Force a complete rewrite rules flush flush_rewrite_rules(true); // One-time fix: Update all existing quotes to have proper slugs if (!get_option('easy_invoice_quotes_slugs_fixed_v3', false)) { global $wpdb; $quote_posts = $wpdb->get_results($wpdb->prepare( "SELECT ID, post_title, post_name FROM {$wpdb->posts} WHERE post_type = %s", \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE )); foreach ($quote_posts as $post) { // Generate a proper slug if empty or doesn't match our format $post_name = $post->post_name ?? ''; if (empty($post_name) || strpos($post_name, 'easy-invoice-quote') === false) { $slug = sanitize_title($post->post_title ?: 'easy-invoice-quote-' . $post->ID); wp_update_post([ 'ID' => $post->ID, 'post_name' => $slug ]); } } update_option('easy_invoice_quotes_slugs_fixed_v3', true); } // One-time fix: Publish all draft quotes to ensure proper permalinks if (!get_option('easy_invoice_quotes_published_v1', false)) { global $wpdb; $draft_quotes = $wpdb->get_results($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'draft'", \EasyInvoice\Constants\PostTypes::EASY_INVOICE_QUOTE_POST_TYPE )); foreach ($draft_quotes as $quote) { wp_update_post([ 'ID' => $quote->ID, 'post_status' => 'publish' ]); } update_option('easy_invoice_quotes_published_v1', true); } // Ensure all quotes have proper slugs foreach ($quotes as $quote): $quote->ensureProperSlug(); ?>
Quote Client Amount Status Issue Date Expiry Date Logs Actions

No quotes found

Get started by creating your first quote.

getNumber() ?: 'QT-' . $quote->getId()); ?>
View Edit getStatus() === 'cancelled'): ?> Restore Delete Permanently Trash Download Export Duplicate $action_html) { echo '' . $action_html . ''; } ?>
getClientId() > 0) { $client_repository = \EasyInvoice\Providers\ClientServiceProvider::getClientRepository(); $client = $client_repository->find($quote->getClientId()); if ($client) { $client_name = $client->getBusinessClientName() ?: ($client->getFirstName() . ' ' . $client->getLastName()); if (empty(trim($client_name))) { $client_name = $client->getDisplayName() ?: 'Client ' . $client->getId(); } } } elseif ($quote->getCustomerName()) { $client_name = $quote->getCustomerName(); } echo esc_html($client_name); ?>
getCustomerEmail() ?: ''); ?>
format($quote->getTotal() ?: 0)); ?> getStatus() ?: 'draft'; $status_classes = [ 'draft' => 'bg-gray-100 text-gray-800 ring-1 ring-gray-300', 'sent' => 'bg-blue-100 text-blue-800 ring-1 ring-blue-300', 'accepted' => 'bg-green-100 text-green-800 ring-1 ring-green-300', 'declined' => 'bg-red-100 text-red-800 ring-1 ring-red-300', 'expired' => 'bg-yellow-100 text-yellow-800 ring-1 ring-yellow-300' ]; $status_class = $status_classes[$status] ?? 'bg-gray-100 text-gray-800 ring-1 ring-gray-300'; ?> getIssueDate() ? date('M j, Y', strtotime($quote->getIssueDate())) : '—'; ?> getExpiryDate() ? date('M j, Y', strtotime($quote->getExpiryDate())) : '—'; ?> getLogs($quote->getId()); $logs_count = count($all_logs); if ($logs_count > 0): ?> No activity Error loading logs
1): ?>
Showing to of results
Page of
1 ? $pagination['current_page'] - 1 : 1; $next_page = $pagination['current_page'] < $pagination['total_pages'] ? $pagination['current_page'] + 1 : $pagination['total_pages']; $current_url = add_query_arg($_GET, admin_url('admin.php')); $current_url = remove_query_arg('paged', $current_url); $start_page = max(1, $pagination['current_page'] - 2); $end_page = min($pagination['total_pages'], $pagination['current_page'] + 2); if ($pagination['current_page'] > 1): ?> ‹ Previous Next ›