db_onedev = $this->load->database('onedev', true); $this->load->library('ibl_encryptor'); } // GET/POST /tools/birt_proxy/stream_by_code // Gunakan ini untuk flow browser print yang butuh URL langsung, // tapi cache PDP harus tetap dihapus segera setelah PDF di-stream. public function stream_by_code() { if (!$this->isLogin) { $this->sys_error('Invalid Token'); return; } $prm = $this->sys_input; $report_code = trim($prm['report_code'] ?? $prm['code_report'] ?? $prm['code'] ?? ''); $order_id = intval($prm['PT_OrderHeaderID'] ?? $prm['order_id'] ?? 0); $payment_id = intval($prm['PPaymentID'] ?? $prm['payment_id'] ?? 0); $username = trim($prm['PUsername'] ?? ($this->sys_user['M_StaffName'] ?? $this->sys_user['M_UserUsername'] ?? $this->sys_user['userName'] ?? 'system')); if (!$report_code) { $this->sys_error('report_code wajib diisi'); return; } if ($order_id <= 0 && $payment_id > 0) { $order_id = $this->_resolve_order_id_by_payment($payment_id); } if ($order_id <= 0) { $this->sys_error('order_id tidak ditemukan'); return; } $cache_id = $this->_populate_cache($order_id); $patient_name = $this->_resolve_patient_name_by_cache($cache_id); if ($patient_name === '') { $patient_name = $this->_resolve_patient_name_by_order($order_id); } $row = $this->db_onedev->query( "SELECT Print_TransactionUrl FROM print_transaction WHERE Print_TransactionCode = ? LIMIT 1", [$report_code] )->row_array(); if (!$row) { $this->_delete_cache($cache_id); $this->sys_error("Report code tidak ditemukan: {$report_code}"); return; } $tm = round(microtime(true) * 1000); $url = $row['Print_TransactionUrl']; $is_internal_app_url = $this->_is_internal_app_url($url); $url = str_replace('PT_OrderHeaderID', $order_id, $url); $url = str_replace('PPaymentID', $payment_id, $url); $url = str_replace('PAn', $this->_format_report_string_param($patient_name, $is_internal_app_url), $url); $url = str_replace('PUsername', $this->_format_report_string_param($username, $is_internal_app_url), $url); $url = str_replace('TS', $tm, $url); $full_url = $this->_resolve_fetch_url($url); $context = stream_context_create([ 'http' => [ 'timeout' => 120, 'method' => 'GET', ] ]); $pdf = @file_get_contents($full_url, false, $context); $this->_delete_cache($cache_id); if ($pdf === false) { $this->sys_error('Gagal generate report dari BIRT server'); return; } $filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf'; header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="' . $filename . '"'); header('Content-Length: ' . strlen($pdf)); echo $pdf; exit; } // POST /tools/birt_proxy/stream public function stream() { if (!$this->isLogin) { $this->sys_error('Invalid Token'); return; } $prm = $this->sys_input; $report_code = $prm['report_code'] ?? ''; $order_id = intval($prm['PT_OrderHeaderID'] ?? 0); $username = $prm['PUsername'] ?? ($this->sys_user['userName'] ?? 'system'); $tm = round(microtime(true) * 1000); if (!$report_code) { $this->sys_error('report_code wajib diisi'); return; } // 1. Ambil URL template dari print_transaction $row = $this->db_onedev->query( "SELECT Print_TransactionUrl FROM print_transaction WHERE Print_TransactionCode = ? LIMIT 1", [$report_code] )->row_array(); if (!$row) { $this->sys_error("Report code tidak ditemukan: {$report_code}"); return; } $url = $row['Print_TransactionUrl']; $is_internal_app_url = $this->_is_internal_app_url($url); $url = str_replace('PT_OrderHeaderID', $order_id, $url); $url = str_replace('PUsername', $this->_format_report_string_param($username, $is_internal_app_url), $url); $url = str_replace('TS', $tm, $url); // 2. Decrypt patient PII dan populate cache $cache_id = null; if ($order_id > 0) { $cache_id = $this->_populate_cache($order_id); } // 3. Build full URL sesuai target endpoint dan fetch PDF $full_url = $this->_resolve_fetch_url($url); $context = stream_context_create([ 'http' => [ 'timeout' => 120, 'method' => 'GET', ] ]); $pdf = @file_get_contents($full_url, false, $context); // 4. Hapus cache if ($cache_id) { $this->db_onedev->query( "DELETE FROM patient_print_cache WHERE ppc_id = ?", [$cache_id] ); } if ($pdf === false) { $this->sys_error('Gagal generate report dari BIRT server'); return; } // 5. Stream PDF ke frontend $filename = $report_code . '_' . $order_id . '_' . date('Ymd') . '.pdf'; header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="' . $filename . '"'); header('Content-Length: ' . strlen($pdf)); echo $pdf; exit; } // Hanya return URL (untuk iframe/window.open) — tanpa stream // Frontend membuka URL ini secara langsung public function get_url() { if (!$this->isLogin) { $this->sys_error('Invalid Token'); return; } $prm = $this->sys_input; $report_code = $prm['report_code'] ?? ''; $order_id = intval($prm['PT_OrderHeaderID'] ?? 0); $username = $prm['PUsername'] ?? ($this->sys_user['userName'] ?? 'system'); $tm = round(microtime(true) * 1000); $row = $this->db_onedev->query( "SELECT Print_TransactionUrl FROM print_transaction WHERE Print_TransactionCode = ? LIMIT 1", [$report_code] )->row_array(); if (!$row) { $this->sys_error("Report code tidak ditemukan: {$report_code}"); return; } $url = $row['Print_TransactionUrl']; $url = str_replace('PT_OrderHeaderID', $order_id, $url); $url = str_replace('PUsername', urlencode($username), $url); $url = str_replace('TS', $tm, $url); // Pre-populate cache — frontend buka URL langsung ke BIRT // Cache hidup 5 menit, cukup untuk BIRT generate PDF if ($order_id > 0) { $this->_populate_cache($order_id); } $this->sys_ok(['url' => $url]); } // Decrypt patient PII dan simpan ke cache private function _populate_cache($order_id) { // Ambil _enc columns dari m_patient via t_orderheader $patient = $this->db_onedev->query( "SELECT M_PatientID, M_PatientName_enc, M_PatientDOB_enc, M_PatientHP_enc, M_PatientEmail_enc, M_PatientDOB FROM t_orderheader JOIN m_patient ON T_OrderHeaderM_PatientID = M_PatientID WHERE T_OrderHeaderID = ? LIMIT 1", [$order_id] )->row_array(); if (!$patient) return null; $addr = $this->db_onedev->query( "SELECT M_PatientAddressDescription_enc FROM m_patientaddress WHERE M_PatientAddressM_PatientID = ? AND M_PatientAddressIsActive = 'Y' AND M_PatientAddressNote = 'Utama' LIMIT 1", [$patient['M_PatientID']] )->row_array(); $enc = $this->ibl_encryptor; $name = $enc->decrypt($patient['M_PatientName_enc'] ?? '') ?? ''; $dob = $enc->decrypt($patient['M_PatientDOB_enc'] ?? '') ?? date('d-m-Y', strtotime($patient['M_PatientDOB'] ?? 'now')); $hp = $enc->decrypt($patient['M_PatientHP_enc'] ?? '') ?? ''; $email= $enc->decrypt($patient['M_PatientEmail_enc']?? '') ?? ''; $address = $enc->decrypt($addr['M_PatientAddressDescription_enc'] ?? '') ?? ''; // Hapus cache lama untuk order ini + cleanup expired $this->db_onedev->query( "DELETE FROM patient_print_cache WHERE ppc_order_id = ? OR ppc_created < NOW() - INTERVAL 5 MINUTE", [$order_id] ); // Insert cache baru $this->db_onedev->query( "INSERT INTO patient_print_cache (ppc_order_id, ppc_patient_id, ppc_name, ppc_dob, ppc_hp, ppc_email, ppc_address, ppc_created) VALUES (?, ?, ?, ?, ?, ?, ?, NOW())", [$order_id, $patient['M_PatientID'], $name, $dob, $hp, $email, $address] ); return $this->db_onedev->insert_id(); } private function _delete_cache($cache_id) { if ($cache_id) { $this->db_onedev->query( "DELETE FROM patient_print_cache WHERE ppc_id = ?", [$cache_id] ); } $this->db_onedev->query( "DELETE FROM patient_print_cache WHERE ppc_created < NOW() - INTERVAL 5 MINUTE" ); } private function _resolve_fetch_url($url) { $url = trim((string) $url); if ($url === '') { return ''; } if (preg_match('#^https?://#i', $url)) { return $url; } if (strpos($url, '/birt/') === 0) { return $this->birt_base . $url; } if (strpos($url, '/one-api-lab/') === 0) { $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; return $scheme . '://' . $host . $url; } if (strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0) { $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; return $scheme . '://' . $host . '/one-api-lab' . $url; } return $this->birt_base . $url; } private function _resolve_order_id_by_payment($payment_id) { $row = $this->db_onedev->query( "SELECT F_PaymentT_OrderHeaderID FROM f_payment WHERE F_PaymentID = ? LIMIT 1", [$payment_id] )->row_array(); return intval($row['F_PaymentT_OrderHeaderID'] ?? 0); } private function _resolve_patient_name_by_cache($cache_id) { if (!$cache_id) { return ''; } $row = $this->db_onedev->query( "SELECT ppc_name FROM patient_print_cache WHERE ppc_id = ? LIMIT 1", [$cache_id] )->row_array(); return trim($row['ppc_name'] ?? ''); } private function _resolve_patient_name_by_order($order_id) { $row = $this->db_onedev->query( "SELECT ppc_name FROM patient_print_cache WHERE ppc_order_id = ? ORDER BY ppc_id DESC LIMIT 1", [$order_id] )->row_array(); return trim($row['ppc_name'] ?? ''); } private function _is_internal_app_url($url) { $url = (string) $url; return ( strpos($url, '/one-api-lab/') === 0 || strpos($url, '/tools/') === 0 || strpos($url, '/index.php/') === 0 ); } private function _format_report_string_param($value, $is_internal_app_url = false) { $value = (string) $value; if ($is_internal_app_url) { return rawurlencode($value); } return rawurlencode("'" . $value . "'"); } }