FHM08062601IBL - tambah Rpt_lab_result FPDF controller untuk generate lab result PDF tanpa BIRT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sas.fajri
2026-06-09 08:39:21 +07:00
parent e797013148
commit c4e590d153

View File

@@ -0,0 +1,362 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Rpt_lab_result — FPDF-based lab result report
* Endpoint: GET /tools/rpt_lab_result/pdf?token=...&PT_OrderHeaderID=...
*
* Menggunakan sp_rpt_hasil_header untuk data pasien,
* query langsung untuk detail hasil pemeriksaan.
* Cache PDP di-populate sebelum call SP lalu dihapus setelah selesai.
*/
class Rpt_lab_result extends MY_Controller
{
private $db_onedev;
public function __construct()
{
parent::__construct();
$this->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=<id>']);
}
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();
}
}
}
}