prepare(" SELECT i.*, c.name as company_name FROM invoices i JOIN companies c ON i.company_id = c.id WHERE i.id = ? AND i.tenant_id = ? "); $stmt->execute([$id, $tenantId]); $invoice = $stmt->fetch(); if (!$invoice) json_error('Invoice not found or access denied', 404); // 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. Fetch JoFotara Submission Data $stmtSub = $db->prepare(" SELECT jofotara_uuid, submitted_at, qr_code_raw, status as submission_status, response_body FROM jofotara_submissions WHERE invoice_id = ? AND status = 'accepted' ORDER BY created_at DESC LIMIT 1 "); $stmtSub->execute([$id]); $submission = $stmtSub->fetch(); $invoice['jofotara'] = $submission ? [ 'uuid' => $submission['jofotara_uuid'], 'submitted_at' => $submission['submitted_at'], 'qr_image_uri' => $submission['qr_code_raw'] ? 'data:image/png;base64,' . $submission['qr_code_raw'] : null, 'has_xml' => true ] : null; // 7. Generate Public URL for File $token = Encryption::encrypt($invoice['original_file_path']); $invoice['file_url'] = '/index.php?route=v1/invoices/file&file_token=' . urlencode($token); json_success($invoice); } catch (\Exception $e) { error_log("Invoice View Error: " . $e->getMessage()); json_error('Server error during invoice retrieval', 500); }