Add Kartu Kontrol Pelayanan FPDF report endpoint

Replaces BIRT rpt_fo_001 which had unresolvable image loading issues.
FPDF-based endpoint with 3.5cm top margin for pre-printed letterhead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sas.fajri
2026-05-24 11:45:59 +07:00
parent 58e50dc498
commit 626af7342e
3 changed files with 9275 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Kartu_kontrol extends MY_Controller
{
private $mediaBase = '/home/one/project/one';
public function __construct()
{
parent::__construct();
$this->db_lab = $this->load->database('onedev', true);
}
public function index()
{
$this->sys_ok(['message' => 'Use /tools/kartu_kontrol/pdf?PID=<id>&username=<name>']);
}
public function pdf()
{
try {
$pid = intval($this->input->get('PID', true));
$username = trim((string) $this->input->get('username', true));
if ($pid <= 0) {
$this->sys_error('PID mandatory');
return;
}
$rows = $this->get_data($pid);
if (empty($rows)) {
$this->sys_error('Data not found for PID ' . $pid);
return;
}
$first = $rows[0];
$printDate = date('M j, Y g:i A');
require_once APPPATH . 'third_party/fpdf/fpdf.php';
$pdf = new FPDF('P', 'mm', 'A4');
$pdf->SetAutoPageBreak(false);
$pdf->SetMargins(10, 35, 10);
$pdf->AddPage();
$this->render_page($pdf, $first, $rows, $username, $printDate);
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="kartu_kontrol_' . $pid . '.pdf"');
echo $pdf->Output('S');
} catch (Exception $e) {
$this->sys_error($e->getMessage());
}
}
private function get_data($pid)
{
$qry = $this->db_lab->query("CALL sp_rpt_fo_001(?, 'fpdf')", [$pid]);
if (!$qry) {
return [];
}
return $qry->result_array();
}
private function render_page($pdf, $first, $rows, $printBy, $printDate)
{
$formRev = $first['form_rev'] ?: 'YAN.01-FRM-CR-01.1/04';
$pageW = 190; // usable width (210 - 20 margin)
$photoW = 25;
$photoH = 30;
$photoX = 10 + $pageW - $photoW; // right-aligned
$topY = 35; // 3.5cm top margin — space for pre-printed logo header
// --- Header ---
$pdf->SetFont('Arial', 'B', 12);
$pdf->SetXY(10, $topY);
$pdf->Cell($pageW - 70, 8, 'KARTU KONTROL PELAYANAN', 0, 0, 'L');
$pdf->SetFont('Arial', '', 8);
$pdf->Cell(70, 8, $this->s($formRev), 0, 1, 'R');
// --- Photo (right side, below form rev) ---
$photoY = $topY + 10;
$photoPath = $this->resolve_photo($first['Photo']);
if ($photoPath !== null) {
$pdf->Image($photoPath, $photoX, $photoY, $photoW, $photoH);
} else {
$pdf->SetDrawColor(180, 180, 180);
$pdf->Rect($photoX, $photoY, $photoW, $photoH);
}
// --- Patient Info ---
$infoY = $topY + 10;
$infoW = $pageW - $photoW - 5;
$lblW = 38;
$sepW = 5;
$valW = $infoW - $lblW - $sepW;
$rowH = 7;
$fields = [
['Nama Pasien', $this->s($first['M_PatientName'])],
['Umur / Jns Kelamin', $first['M_PatientDOB']],
['PID/No. Reg', $first['PIiiD']],
['Pengirim', $this->s($first['M_CompanyName'] ?? '')],
['Kel. Pelanggan', $this->s($first['Nat_BahanName'] ?? '')],
['Tanggal', $first['noreg']],
['Perkiraan Hasil', $this->s($first['M_PatientAddressDescription'] ?? '')],
];
$pdf->SetFont('Arial', '', 9);
foreach ($fields as $f) {
$pdf->SetXY(10, $infoY);
$pdf->Cell($lblW, $rowH, $this->s($f[0]), 0, 0, 'L');
$pdf->Cell($sepW, $rowH, ':', 0, 0, 'C');
$pdf->Cell($valW, $rowH, $this->s($f[1]), 0, 1, 'L');
$infoY += $rowH;
}
// --- Test Table ---
$tableY = max($infoY + 3, $photoY + $photoH + 3);
$col1W = 90;
$col2W = 50;
$col3W = $pageW - $col1W - $col2W;
$thH = 7;
$pdf->SetXY(10, $tableY);
$pdf->SetFont('Arial', 'B', 9);
$pdf->SetFillColor(220, 220, 220);
$pdf->Cell($col1W, $thH, 'JENIS PEMERIKSAAN', 1, 0, 'C', true);
$pdf->Cell($col2W, $thH, 'PETUGAS', 1, 0, 'C', true);
$pdf->Cell($col3W, $thH, 'CATATAN', 1, 1, 'C', true);
$pdf->SetFont('Arial', '', 9);
foreach ($rows as $row) {
$pdf->SetX(10);
$pdf->Cell($col1W, $thH, $this->s($row['Nat_BahanCode']), 1, 0, 'L');
$pdf->Cell($col2W, $thH, '', 1, 0, 'L');
$pdf->Cell($col3W, $thH, '', 1, 1, 'L');
}
// --- Consent Section ---
$consentY = $pdf->GetY() + 4;
$pdf->SetXY(10, $consentY);
$pdf->SetFont('Arial', '', 9);
$pdf->Cell(0, 6, 'Catatan :', 0, 1, 'L');
$consentText = 'Dengan ini saya menyatakan bahwa informasi yang saya berikan adalah benar ,'
. 'dan saya memahami serta menyetujui tindakan medis yang dilakukan oleh Lab. IBL '
. 'untuk menunjang pemeriksaan saya';
$consentY = $pdf->GetY();
$pdf->SetXY(10, $consentY);
$pdf->SetFont('Arial', '', 8);
$pdf->MultiCell(100, 5, $this->s($consentText), 0, 'L');
$pdf->SetFont('Arial', 'B', 9);
$pdf->SetXY(130, $consentY);
$pdf->Cell(70, 5, 'TTD', 0, 1, 'C');
$afterConsent = $pdf->GetY() + 18;
$pdf->SetXY(130, $afterConsent);
$pdf->SetFont('Arial', '', 9);
$pdf->Cell(70, 5, $this->s($first['PIiiD']), 0, 1, 'C');
// --- Page Footer ---
$footerY = 286;
$pdf->SetFont('Times', '', 7);
$pdf->SetXY(10, $footerY);
$pdf->Cell(60, 4, 'Print Oleh : ' . $this->s($printBy), 0, 0, 'L');
$pdf->SetXY(85, $footerY);
$pdf->Cell(20, 4, '1', 0, 0, 'C');
$pdf->Cell(5, 4, '/', 0, 0, 'C');
$pdf->Cell(20, 4, '1', 0, 0, 'C');
$pdf->SetXY(130, $footerY);
$pdf->Cell(70, 4, $printDate, 0, 1, 'R');
}
private function resolve_photo($photoUrl)
{
$url = trim((string) $photoUrl);
if ($url === '') {
return null;
}
// SP returns file:///home/one/project/one/... — strip file:// to get absolute path
if (strpos($url, 'file://') === 0) {
$abs = substr($url, 7);
return is_file($abs) ? $abs : null;
}
// fallback: prepend mediaBase for relative paths
$abs = $this->mediaBase . '/' . ltrim($url, '/');
return is_file($abs) ? $abs : null;
}
private function s($text)
{
return iconv('UTF-8', 'windows-1252//TRANSLIT', (string) $text);
}
}