prepare(" SELECT i.*, c.name as company_name FROM invoices i LEFT JOIN companies c ON i.company_id = c.id WHERE i.id = ? "); $stmt->execute([$id]); $invoice = $stmt->fetch(); if (!$invoice) { json_error('Invoice not found', 404); } // 3. Authorization Check if ($decoded['role'] !== 'super_admin') { if ($invoice['tenant_id'] !== $decoded['tenant_id']) { json_error('Unauthorized access to this invoice', 403); } } // 4. Fetch Line Items $stmtLines = $db->prepare("SELECT * FROM invoice_lines WHERE invoice_id = ? ORDER BY line_number ASC"); $stmtLines->execute([$id]); $invoice['items'] = $stmtLines->fetchAll(); // 5. Decrypt Fields (Robustly) $decrypt = fn($val) => Encryption::decrypt($val ?? '') ?: $val; $invoice['supplier_tin'] = $decrypt($invoice['supplier_tin']); $invoice['supplier_name'] = $decrypt($invoice['supplier_name']); $invoice['supplier_address'] = $decrypt($invoice['supplier_address']); $invoice['buyer_tin'] = $decrypt($invoice['buyer_tin']); $invoice['buyer_name'] = $decrypt($invoice['buyer_name']); $invoice['buyer_national_id'] = $decrypt($invoice['buyer_national_id']); if (!empty($invoice['company_name'])) { $invoice['company_name'] = $decrypt($invoice['company_name']); } // 6. Generate Public URL for File (Assuming storage is symlinked or served) // For now, let's just return the relative path or a proxy route // We'll add a proxy route later if needed. $invoice['file_url'] = '/index.php?route=v1/invoices/file&id=' . $invoice['id']; json_success($invoice); } catch (\Exception $e) { json_error('Error fetching invoice: ' . $e->getMessage(), 500); }