prepare(" SELECT p.id, p.project_id as project_code, p.client_id FROM projects p LEFT JOIN invoices i ON i.project_id = p.id WHERE p.client_id = ? AND p.status = 'Closed' AND i.id IS NULL "); $stmt->execute([$client_id]); foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $m) { generateProjectInvoice($pdo, $m['id'], $m['project_code'], $m['client_id']); } } catch (Exception $e) { error_log("Invoice auto-gen: " . $e->getMessage()); } // Update statuses updateInvoiceStatuses($pdo); // Filters $fStatus = $_GET['status'] ?? ''; $fDateFrom = $_GET['date_from'] ?? ''; $fDateTo = $_GET['date_to'] ?? ''; $fDueFrom = $_GET['due_from'] ?? ''; $fDueTo = $_GET['due_to'] ?? ''; $fSearch = trim($_GET['search'] ?? ''); $sortBy = $_GET['sort'] ?? 'invoice_date'; $sortDir = ($_GET['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC'; $allowed = ['invoice_number','invoice_date','due_date','total_amount','status','project_code']; if (!in_array($sortBy, $allowed)) $sortBy = 'invoice_date'; $where = ["i.client_id = ?"]; $params = [$client_id]; if ($fStatus) { $where[] = "i.status = ?"; $params[] = $fStatus; } if ($fDateFrom) { $where[] = "i.invoice_date >= ?"; $params[] = $fDateFrom; } if ($fDateTo) { $where[] = "i.invoice_date <= ?"; $params[] = $fDateTo; } if ($fDueFrom) { $where[] = "i.due_date >= ?"; $params[] = $fDueFrom; } if ($fDueTo) { $where[] = "i.due_date <= ?"; $params[] = $fDueTo; } if ($fSearch) { $where[] = "(i.invoice_number LIKE ? OR p.project_name LIKE ? OR i.project_code LIKE ?)"; $s="%$fSearch%"; $params=array_merge($params,[$s,$s,$s]); } $whereClause = implode(' AND ', $where); // === CSV Export (must run before any HTML output) === if (isset($_GET['export']) && $_GET['export'] === 'csv') { try { $csvSortMap = ['invoice_number'=>'i.invoice_number','invoice_date'=>'i.invoice_date','due_date'=>'i.due_date','total_amount'=>'i.total_amount','status'=>'i.status','project_code'=>'i.project_code']; $csvSort = $csvSortMap[$sortBy] ?? 'i.invoice_date'; $stmt = $pdo->prepare(" SELECT i.*, p.project_name, p.industry FROM invoices i INNER JOIN projects p ON i.project_id = p.id WHERE $whereClause ORDER BY $csvSort $sortDir "); $stmt->execute($params); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="my_invoices_'.date('Y-m-d_His').'.csv"'); $out = fopen('php://output', 'w'); fprintf($out, chr(0xEF).chr(0xBB).chr(0xBF)); fputcsv($out, ['Invoice #','Project','Project ID','Industry','Currency','Completes','Avg LOI','Rate/Sample','Total Amount','Invoice Date','Due Date','Paid Date','Status','Payment Ref']); foreach ($rows as $r) { fputcsv($out, [ $r['invoice_number']??'', $r['project_name']??'', $r['project_code']??'', $r['industry']??'', $r['currency']??'', $r['valid_completes']??'', $r['avg_loi_minutes']??'', $r['rate_per_sample']??'', $r['total_amount']??'', $r['invoice_date']??'', $r['due_date']??'', $r['paid_date']??'', $r['status']??'', $r['payment_reference']??'' ]); } fclose($out); exit; } catch (Exception $e) { error_log("Client CSV Export error: " . $e->getMessage()); } } // Fetch invoices $stmt = $pdo->prepare(" SELECT i.*, p.project_name, p.industry FROM invoices i INNER JOIN projects p ON i.project_id = p.id WHERE $whereClause ORDER BY $sortBy $sortDir "); $stmt->execute($params); $invoices = $stmt->fetchAll(PDO::FETCH_ASSOC); $sumAll=$sumPaid=$sumOut=0; foreach ($invoices as $inv) { $sumAll += $inv['total_amount']; if ($inv['status']==='paid') $sumPaid += $inv['total_amount']; else $sumOut += $inv['total_amount']; } $currSymbol = !empty($invoices) ? ($invoices[0]['currency_symbol'] ?? '₹') : '₹'; include 'client-portal-header.php'; ?>

📄 Invoices

View invoices for all your closed projects

📥 Download Invoices
Total Invoiced
Total Paid
Outstanding
Clear
📄

No invoices found. Invoices are automatically generated when projects are closed.

'','dir'=>''])); ?>
Invoice # Project Industry Completes Avg LOI Total Amount Invoiced Due (Net 30) Status PDF
min 📄 PDF

* All amounts are inclusive of applicable taxes