From c4e590d153cdb21aa9948e88209bb38e534ab9e6 Mon Sep 17 00:00:00 2001 From: "sas.fajri" Date: Tue, 9 Jun 2026 08:39:21 +0700 Subject: [PATCH] FHM08062601IBL - tambah Rpt_lab_result FPDF controller untuk generate lab result PDF tanpa BIRT Co-Authored-By: Claude Sonnet 4.6 --- .../controllers/tools/Rpt_lab_result.php | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 application/controllers/tools/Rpt_lab_result.php diff --git a/application/controllers/tools/Rpt_lab_result.php b/application/controllers/tools/Rpt_lab_result.php new file mode 100644 index 00000000..c8cc5576 --- /dev/null +++ b/application/controllers/tools/Rpt_lab_result.php @@ -0,0 +1,362 @@ +db_onedev = $this->load->database('onedev', true); + $this->load->library('ibl_patient_decrypt'); + } + + public function index() + { + $this->sys_ok(['message' => 'Use /tools/rpt_lab_result/pdf?PT_OrderHeaderID=']); + } + + public function pdf() + { + try { + $order_id = $this->_get_order_id(); + if ($order_id <= 0) { + $this->sys_error('PT_OrderHeaderID wajib diisi'); + return; + } + + $username = trim($this->input->get('username', true) ?? $this->input->post('username', true) ?? ''); + if ($username === '') { + $username = $this->sys_user['M_StaffName'] ?? $this->sys_user['M_UserUsername'] ?? 'ADMIN'; + } + + // Populate cache PDP + $cache_id = $this->ibl_patient_decrypt->populate_cache_by_order($order_id); + + // Header data via SP + $header = $this->_fetch_header($order_id, $username); + if (!$header) { + $this->ibl_patient_decrypt->delete_cache($cache_id); + $this->sys_error('Data order tidak ditemukan'); + return; + } + + // Detail hasil pemeriksaan + $details = $this->_fetch_details($order_id); + + // Company info (address, phone IBL) + $company_info = $this->_fetch_company_info(); + + // Delete cache setelah data diambil + $this->ibl_patient_decrypt->delete_cache($cache_id); + + // Generate PDF + $pdf_bytes = $this->_build_pdf($header, $details, $company_info, $username); + + $filename = 'lab_result_' . $order_id . '_' . date('Ymd') . '.pdf'; + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; filename="' . $filename . '"'); + header('Content-Length: ' . strlen($pdf_bytes)); + echo $pdf_bytes; + exit; + + } catch (Exception $e) { + $this->sys_error($e->getMessage()); + } + } + + // ------------------------------------------------------------------------- + // Data fetching + // ------------------------------------------------------------------------- + + private function _fetch_header($order_id, $username) + { + $qry = $this->db_onedev->query('CALL sp_rpt_hasil_header(?, ?)', [$order_id, $username]); + if (!$qry) return null; + $row = $qry->row_array(); + $this->_clean_multi_result(); + return $row ?: null; + } + + private function _fetch_details($order_id) + { + $sql = "SELECT + IFNULL(gr.Group_ResultName, '') AS group_name, + IFNULL(gr.Group_ResultSort, 999) AS group_sort, + IFNULL(grd.Group_ResultDetailSort, 999) AS detail_sort, + tt.T_TestName AS test_name, + IFNULL(tod.T_OrderDetailResult, '') AS result, + CASE + WHEN nv.Nat_NormalValueMin IS NOT NULL AND nv.Nat_NormalValueMin != '' + AND nv.Nat_NormalValueMax IS NOT NULL AND nv.Nat_NormalValueMax != '' + THEN CONCAT(nv.Nat_NormalValueMin, ' - ', nv.Nat_NormalValueMax) + WHEN nv.Nat_NormalValueText IS NOT NULL AND nv.Nat_NormalValueText != '' + THEN nv.Nat_NormalValueText + ELSE IFNULL(tod.T_OrderDetailNormalValueNote, '') + END AS normal_range, + IFNULL(nt.Nat_TestUnit, '') AS unit, + IFNULL(nm.Nat_MethodeName, '') AS method, + IFNULL(tod.T_OrderDetailIsAbnormal, 'N') AS is_abnormal + FROM t_orderdetail tod + JOIN t_test tt ON tod.T_OrderDetailT_TestID = tt.T_TestID AND tt.T_TestIsResult = 'Y' + JOIN nat_test nt ON tt.T_TestNat_TestID = nt.Nat_TestID + LEFT JOIN nat_normalvalue nv ON tod.T_OrderDetailNat_NormalValueID = nv.Nat_NormalValueID + LEFT JOIN nat_methode nm ON tod.T_OrderDetailNat_MethodeID = nm.Nat_MethodeID + LEFT JOIN group_resultdetail grd + ON grd.Group_ResultDetailT_TestID = tt.T_TestID AND grd.Group_ResultDetailIsActive = 'Y' + LEFT JOIN group_result gr + ON grd.Group_ResultDetailGroup_ResultID = gr.Group_ResultID + AND gr.Group_ResultIsActive = 'Y' + AND gr.Group_ResultFlagNonLab = 'N' + WHERE tod.T_OrderDetailT_OrderHeaderID = ? + AND tod.T_OrderDetailIsActive = 'Y' + AND tod.T_OrderDetailResult IS NOT NULL + AND tod.T_OrderDetailResult != '' + ORDER BY gr.Group_ResultSort, grd.Group_ResultDetailSort"; + + $qry = $this->db_onedev->query($sql, [$order_id]); + return $qry ? $qry->result_array() : []; + } + + private function _fetch_company_info() + { + $row = $this->db_onedev->query( + "SELECT S_SystemsCompanyAddress, S_SystemsCompanyPhone, S_SystemsCompanyName FROM s_systems LIMIT 1" + )->row_array(); + return $row ?: []; + } + + // ------------------------------------------------------------------------- + // PDF builder + // ------------------------------------------------------------------------- + + private function _build_pdf(array $h, array $details, array $co, $username) + { + require_once APPPATH . 'third_party/fpdf/fpdf.php'; + + $pdf = new FPDF('P', 'mm', 'A4'); + $pdf->SetMargins(10, 8, 10); + $pdf->SetAutoPageBreak(true, 10); + $pdf->AddPage(); + + $pw = $pdf->GetPageWidth(); // 210 + $ml = 10; + $mr = 10; + $cw = $pw - $ml - $mr; // 190 + + // ── Page header ────────────────────────────────────────────────────── + $company_addr = $this->_s($co['S_SystemsCompanyAddress'] ?? 'Jl. LL. RE. Martadinata No. 135 Bandung'); + $company_phone = $this->_s($co['S_SystemsCompanyPhone'] ?? '(022)7271946'); + $addr_line = $company_addr . ' Telp. ' . $company_phone; + + $pj_label = 'Penanggung Jawab : '; + $pj_name = $this->_s($h['M_DoctorName'] ?? ''); + + $pdf->SetFont('Arial', '', 8); + $pdf->SetXY($ml, 8); + $pdf->Cell($cw / 2, 5, $addr_line, 0, 0, 'L'); + $pdf->SetX($ml + $cw / 2); + $pdf->Cell($cw / 2, 5, $pj_label . $pj_name, 0, 1, 'R'); + + $pdf->SetX($ml); + $pdf->Cell($cw, 0.3, '', 'T', 1); // horizontal line + $pdf->Ln(1); + + // ── Patient info (2 columns) ────────────────────────────────────────── + $lw = $cw * 0.50; // left column width + $rw = $cw * 0.50; // right column width + $lbl_w = 32; + $col_w = 4; + $val_w = $lw - $lbl_w - $col_w; + + $lbl_rw = 34; + $val_rw = $rw - $lbl_rw - $col_w; + + $noReg = $this->_s($h['M_PatientNoReg'] ?? '-'); + $nama = $this->_s($h['M_PatientName'] ?? '-'); + $pengirim = '-'; + $kelPel = $this->_s($h['CorporateName'] ?? '-'); + $alamat = $this->_s($h['M_PatientAddress'] ?? '-'); + + $tglReg = $this->_s($h['T_OrderHeaderDate'] ?? '-'); + $pid = $this->_s($h['T_OrderHeaderLabNumber'] ?? '-'); + $jk = $this->_s($h['Gender'] ?? '-'); + $tglLahir = $this->_s($h['M_PatientDOB'] ?? '-'); + $usia = $this->_s($h['T_OrderHeaderM_PatientAge'] ?? '-'); + $hp = $this->_s($h['M_PatientHp'] ?? '-'); + + $left_rows = [ + ['NO. REG', $noReg], + ['NAMA', $nama], + ['PENGIRIM', $pengirim], + ['KEL. PELANGGAN*',$kelPel], + ['ALAMAT', $alamat], + ]; + $right_rows = [ + ['TANGGAL REG', $tglReg], + ['PID', $pid], + ['JENIS KELAMIN', $jk], + ['TGL. LAHIR / USIA', $tglLahir . ' / ' . $usia], + ['NO. TLP. / HP', $hp], + ]; + + $pdf->SetFont('Arial', '', 8); + $y_info_start = $pdf->GetY(); + + // Barcode area (top-right, above right column rows) + $pdf->SetXY($ml + $lw + 2, $y_info_start); + $pdf->SetFont('Arial', 'B', 7); + $pdf->Cell($rw - 2, 5, '||| ' . $pid . ' |||', 0, 1, 'C'); + $pdf->SetFont('Arial', '', 8); + + $y_right_start = $pdf->GetY(); + $y_left = $y_info_start; + $y_right = $y_right_start; + + // Left column + foreach ($left_rows as $row) { + $y_left = $pdf->GetY(); + $pdf->SetXY($ml, $y_left); + $pdf->SetFont('Arial', '', 8); + $pdf->Cell($lbl_w, 5, $row[0], 0, 0); + $pdf->Cell($col_w, 5, ':', 0, 0, 'C'); + $pdf->SetFont('Arial', '', 8); + $y0 = $pdf->GetY(); + $pdf->SetXY($ml + $lbl_w + $col_w, $y0); + $pdf->MultiCell($val_w, 5, $row[1], 0, 'L'); + } + + // Right column + $pdf->SetY($y_right); + foreach ($right_rows as $row) { + $y_right = $pdf->GetY(); + $pdf->SetXY($ml + $lw, $y_right); + $pdf->SetFont('Arial', '', 8); + $pdf->Cell($lbl_rw, 5, $row[0], 0, 0); + $pdf->Cell($col_w, 5, ':', 0, 0, 'C'); + $pdf->SetFont('Arial', '', 8); + $y0 = $pdf->GetY(); + $pdf->SetXY($ml + $lw + $lbl_rw + $col_w, $y0); + $pdf->MultiCell($val_rw, 5, $row[1], 0, 'L'); + } + + $pdf->SetY(max($pdf->GetY(), $y_left) + 2); + $pdf->SetX($ml); + $pdf->Cell($cw, 0.3, '', 'T', 1); + $pdf->Ln(1); + + // ── Table header ───────────────────────────────────────────────────── + $col = ['JENIS PEMERIKSAAN' => 67, 'HASIL' => 25, 'NILAI RUJUKAN' => 51, 'SATUAN' => 20, 'METODE' => 27]; + $row_h = 5; + + $pdf->SetFont('Arial', 'B', 8); + $pdf->SetFillColor(240, 240, 240); + $pdf->SetX($ml); + foreach ($col as $label => $w) { + $pdf->Cell($w, $row_h + 1, $label, 'LRB', 0, 'C', true); + } + $pdf->Ln(); + + // ── Table rows ──────────────────────────────────────────────────────── + $prev_group = null; + $pdf->SetFont('Arial', '', 8); + + foreach ($details as $d) { + $group = strtoupper(trim($d['group_name'])); + + // Group header row + if ($group !== '' && $group !== $prev_group) { + $pdf->SetX($ml); + $pdf->SetFont('Arial', 'B', 8); + $pdf->Cell($cw, $row_h, $group, 'LR', 1, 'L'); + $prev_group = $group; + } + + // Test row + $test_name = $this->_s($d['test_name']); + $result = $this->_s($d['result']); + $normal = $this->_s($d['normal_range']); + $unit = $this->_s($d['unit']); + $method = $this->_s($d['method']); + $is_abnormal = ($d['is_abnormal'] === 'Y'); + + $pdf->SetX($ml); + if ($is_abnormal) { + $pdf->SetFont('Arial', 'B', 8); + } else { + $pdf->SetFont('Arial', '', 8); + } + + // JENIS PEMERIKSAAN — pakai MultiCell, sinkronkan Y manual + $x_start = $pdf->GetX(); + $y_start = $pdf->GetY(); + + $col_widths = array_values($col); + $col_data = [$test_name, $result, $normal, $unit, $method]; + $col_aligns = ['L', 'C', 'C', 'C', 'L']; + + // Hitung tinggi baris dari MultiCell terpanjang + $row_height = $row_h; + foreach ($col_data as $i => $text) { + $lines = $pdf->GetStringWidth($text) > ($col_widths[$i] - 2) + ? ceil($pdf->GetStringWidth($text) / ($col_widths[$i] - 2)) + : 1; + $row_height = max($row_height, $lines * $row_h); + } + + foreach ($col_data as $i => $text) { + $pdf->SetXY($x_start + array_sum(array_slice($col_widths, 0, $i)), $y_start); + $pdf->Cell($col_widths[$i], $row_height, $text, 'LR', 0, $col_aligns[$i]); + } + $pdf->SetXY($ml, $y_start + $row_height); + + // Bottom border seluruh row + $pdf->SetX($ml); + $pdf->Cell($cw, 0, '', 'T', 1); + + $pdf->SetFont('Arial', '', 8); + } + + // Tutup tabel + $pdf->SetX($ml); + $pdf->Cell($cw, 0, '', 'T', 1); + + return $pdf->Output('S'); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private function _get_order_id() + { + foreach (['PT_OrderHeaderID', 'PID', 'order_id'] as $k) { + $v = $this->input->get($k, true) ?? $this->input->post($k, true) ?? ($this->sys_input[$k] ?? null); + if ($v !== null && $v !== '') return intval($v); + } + return 0; + } + + private function _s($text) + { + return iconv('UTF-8', 'windows-1252//TRANSLIT', (string) $text); + } + + private function _clean_multi_result() + { + if (isset($this->db_onedev->conn_id) && $this->db_onedev->conn_id instanceof mysqli) { + while ($this->db_onedev->conn_id->more_results()) { + $this->db_onedev->conn_id->next_result(); + } + } + } +}