Files
BE_IBL/application/libraries/Genreportfisik.php
2026-04-15 15:16:12 +07:00

1050 lines
45 KiB
PHP

<?php
defined("BASEPATH") or exit("No direct script access allowed");
class Genreportfisik
{
/** @var object CodeIgniter DB instance (public agar bisa di-inject dari controller) */
public $db_smartone;
function __construct()
{
$CI = & get_instance();
$this->db_smartone = $CI->load->database("default", true);
}
function clean_mysqli_connection($dbc)
{
while (mysqli_more_results($dbc)) {
if (mysqli_next_result($dbc)) {
$result = mysqli_use_result($dbc);
if (get_class($result) == 'mysqli_stmt') {
mysqli_stmt_free_result($result);
}
else {
unset($result);
}
}
}
}
// ============================================================
// ENTRY POINT UTAMA (GET)
// Parameter : $order_header_id = T_OrderHeaderID
// Mengembalikan JSON object berisi semua data fisik yang sudah
// diproses, termasuk dukungan bilingual jika
// T_OrderHeaderAddOnSecondM_LangID > 0
// ============================================================
function get_data($order_header_id)
{
$order_header_id = intval($order_header_id);
if ($order_header_id <= 0) {
echo json_encode(['error' => 'Invalid T_OrderHeaderID']);
return;
}
// -- Cek apakah perlu bilingual --
$sql = "SELECT T_OrderHeaderAddOnSecondM_LangID
FROM t_orderheaderaddon
WHERE T_OrderHeaderAddOnT_OrderHeaderID = ?
AND T_OrderHeaderAddOnIsActive = 'Y'
LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
$addon_row = $qry ? $qry->row_array() : [];
$second_langid = isset($addon_row['T_OrderHeaderAddOnSecondM_LangID'])
? intval($addon_row['T_OrderHeaderAddOnSecondM_LangID'])
: 0;
$result = [];
$result['tanda_vital'] = $this->get_tanda_vital($order_header_id);
$result['status_gizi'] = $this->get_status_gizi($order_header_id);
$result['persepsi_warna'] = $this->get_persepsi_warna($order_header_id);
$result['visus'] = $this->get_visus($order_header_id);
$result['body_fat'] = $this->get_body_fat($order_header_id);
if ($second_langid > 0) {
$result['tanda_vital_lang2'] = $this->get_tanda_vital($order_header_id, $second_langid);
$result['status_gizi_lang2'] = $this->get_status_gizi($order_header_id, $second_langid);
$result['persepsi_warna_lang2'] = $this->get_persepsi_warna($order_header_id, $second_langid);
$result['visus_lang2'] = $this->get_visus($order_header_id, $second_langid);
}
$result['second_langid'] = $second_langid;
echo json_encode($result);
}
// ============================================================
// ENTRY POINT UTAMA (SAVE)
// Membaca semua row so_resultentry_fisik_umum untuk 1 order,
// lalu INSERT ke so_resultentry_fisik_summary (1 tabel).
// • tanda_vital → 1 baris per item pengukuran
// • template lain → 1 baris per item yang abnormal (dicentang+tidak normal)
//
// Idempotent: hapus dulu data lama (IsActive='Y') sebelum insert.
//
// @param int $order_header_id T_OrderHeaderID
// @param int $user_id M_UserID
// @return array ['success'=>bool, 'saved'=>int, 'errors'=>array]
// ============================================================
function save_data($order_header_id, $user_id = 0)
{
$order_header_id = intval($order_header_id);
$result = [
'success' => false,
'saved' => 0,
'errors' => [],
];
if ($order_header_id <= 0) {
$result['errors'][] = 'Invalid T_OrderHeaderID';
return $result;
}
// -- 1. Ambil semua template yang sudah diisi untuk order ini --
$sql = "SELECT
srfu.*,
ft.FisikTemplateCode,
ft.FisikTemplateType,
ft.FisikTemplateTableName,
ft.FisikTemplateTitle
FROM so_resultentry_fisik_umum srfu
JOIN fisik_template ft
ON srfu.So_ResultEntryFisikUmumFisikTemplateID = ft.FisikTemplateID
AND ft.FisikTemplateIsActive = 'Y'
WHERE srfu.So_ResultEntryFisikUmumT_OrderHeaderID = ?
AND srfu.So_ResultEntryFisikUmumIsActive = 'Y'";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry) {
$result['errors'][] = 'Query so_resultentry_fisik_umum gagal';
return $result;
}
$rows = $qry->result_array();
if (count($rows) == 0) {
$result['errors'][] = 'Tidak ada data fisik untuk order ini';
return $result;
}
// -- 2. Hapus data lama (soft-delete dulu, lalu truncate baris lama) --
// Gunakan hard-delete (DELETE) untuk idempotent yang bersih,
// karena data lama sudah di-soft-delete saat unval1.
$this->db_smartone->query(
"DELETE FROM so_resultentry_fisik_summary
WHERE So_ResultEntryFisikSummaryT_OrderHeaderID = ?",
[$order_header_id]
);
$now = date('Y-m-d H:i:s');
// -- 3. Proses tiap template --
foreach ($rows as $row) {
$json_str = $row['So_ResultEntryFisikUmumDetails'];
$template_id = intval($row['So_ResultEntryFisikUmumFisikTemplateID']);
$tmpl_code = $row['FisikTemplateCode'];
$tmpl_title = $row['FisikTemplateTitle'];
$tmpl_table = $row['FisikTemplateTableName'];
$d = json_decode($json_str, true);
if (!$d) {
$result['errors'][] = "JSON invalid untuk template {$tmpl_code}";
continue;
}
$type_form = strtoupper($d['type_form'] ?? '');
$is_tanda_vital = (
$tmpl_table === 'tanda_vital' ||
strtoupper($tmpl_title) === 'TANDA VITAL' ||
in_array($tmpl_code, ['FI00', 'FI00.TD'])
);
if ($is_tanda_vital) {
// -- Tanda Vital: 1 baris per item pengukuran --
$tv_items = $this->build_tanda_vital_rows($d);
foreach ($tv_items as $tv) {
$ok = $this->insert_summary_row(
$order_header_id, $template_id, $tmpl_code, $tmpl_table,
$tv['id_code'], $tv['label'], $tv['label'],
$tv['value'], $tv['value2'],
$tv['value_numeric'], $tv['value2_numeric'],
null, null, null,
'tanda_vital',
$user_id, $now
);
if ($ok) $result['saved']++;
}
continue;
}
// -- Template lain: simpan semua item yang dicentang --
// Aturan:
// • is_normal='Y' + chx=true → type='normal'
// • is_normal='N' + chx=true → type='kelainan'
// • XXV chx_y=true → type='normal'
// • XXV chx_x=true → type='kelainan'
// • Riwayat/K3: chx=true → type='kelainan'
// • Riwayat/K3: tidak ada yg dicentang → 1 baris 'Tidak Ada' type='normal'
$details = $d['details'] ?? [];
$tmpl_type = strtoupper($d['type_form'] ?? '');
$is_riwayat = in_array($tmpl_type, ['XV', 'XVS', 'XO', 'XD', 'XVS-LXX'])
&& !isset($d['flag_normal']) // Fisik punya flag_normal, Riwayat tidak
&& ($tmpl_type !== 'XV' || !$this->has_is_normal_field($details));
// Lebih tepat: Riwayat tidak punya is_normal di items-nya
$is_riwayat_or_k3 = $this->is_riwayat_or_k3_template($details, $tmpl_type);
$any_checked = false;
$saved_in_tmpl = 0;
foreach ($details as $item) {
if (isset($item['details']) && is_array($item['details'])) {
$segment = $item['name'] ?? ($item['segment_name'] ?? null);
foreach ($item['details'] as $sub_item) {
$sub_item['_segment'] = $segment;
$saved = $this->save_item_by_rule(
$order_header_id, $template_id, $tmpl_code,
$tmpl_table, $tmpl_type, $tmpl_title, $sub_item, $user_id, $now
);
if ($saved) { $result['saved']++; $saved_in_tmpl++; $any_checked = true; }
}
}
else {
$saved = $this->save_item_by_rule(
$order_header_id, $template_id, $tmpl_code,
$tmpl_table, $tmpl_type, $tmpl_title, $item, $user_id, $now
);
if ($saved) { $result['saved']++; $saved_in_tmpl++; $any_checked = true; }
}
}
// -- Jika Riwayat/K3 tidak ada yang dicentang → simpan 'Tidak Ada' --
if ($is_riwayat_or_k3 && !$any_checked) {
$ok = $this->insert_summary_row(
$order_header_id, $template_id, $tmpl_code, $tmpl_table,
'tidak_ada', 'Tidak Ada', 'Tidak Ada',
null, null, null, null,
null, null, null,
'normal',
$user_id, $now
);
if ($ok) $result['saved']++;
}
}
$result['success'] = true;
return $result;
}
// ============================================================
// Helper: cek apakah template ini Riwayat atau K3
// (bedanya dengan Fisik: items tidak punya is_normal)
// ============================================================
private function is_riwayat_or_k3_template($details, $type_form)
{
if ($type_form === 'XVV') return true; // K3 selalu XVV
// Cek apakah ada item dengan is_normal → berarti Fisik, bukan Riwayat
foreach ($details as $item) {
if (isset($item['is_normal'])) return false;
if (isset($item['details'])) {
foreach ($item['details'] as $sub) {
if (isset($sub['is_normal'])) return false;
}
}
}
return true; // tidak ada is_normal → Riwayat
}
// ============================================================
// Helper: cek apakah detail punya field is_normal (Fisik)
// ============================================================
private function has_is_normal_field($details)
{
foreach ($details as $item) {
if (isset($item['is_normal'])) return true;
if (isset($item['details'])) {
foreach ($item['details'] as $sub) {
if (isset($sub['is_normal'])) return true;
}
}
}
return false;
}
// ============================================================
// SAVE ITEM BY RULE
// Tentukan summary_type berdasarkan is_normal + chx
// Hanya simpan jika item dicentang (chx=true / chx_x / chx_y)
// $tmpl_title dipakai untuk LabelDisplay: "JantungTitle : segment"
// ============================================================
private function save_item_by_rule(
$order_header_id, $template_id, $tmpl_code,
$tmpl_table, $type_form, $tmpl_title, $item, $user_id, $now
) {
$chx = !empty($item['chx']) || $item['chx'] === 1 || $item['chx'] === '1' || $item['chx'] === true;
$chx_x = !empty($item['chx_x']) || $item['chx_x'] === 1 || $item['chx_x'] === '1' || $item['chx_x'] === true;
$chx_y = !empty($item['chx_y']) || $item['chx_y'] === 1 || $item['chx_y'] === '1' || $item['chx_y'] === true;
$is_normal_field = isset($item['is_normal']) ? strtoupper($item['is_normal']) : null;
$summary_type = null;
// Skip tipe yang tidak masuk summary
$skip_types = ['VXX+', 'VXX', 'V', 'T', 'XXVWL'];
if (in_array($type_form, $skip_types)) return false;
if ($type_form === 'XXV') {
// chx_y = Normal, chx_x = Tidak Normal
if ($chx_y) $summary_type = 'normal';
elseif ($chx_x) $summary_type = 'kelainan';
else return false;
}
elseif ($type_form === 'XVV') {
// K3: chx = ada paparan = kelainan
if (!$chx) return false;
$summary_type = 'kelainan';
}
elseif ($type_form === 'TOOTH') {
if (!$chx) return false;
$summary_type = 'kelainan';
}
else {
// XV, XVS, XVS-LXX, XO, XD, X, Riwayat ...
if (!$chx) return false;
if ($is_normal_field === 'Y') $summary_type = 'normal';
elseif ($is_normal_field === 'N') $summary_type = 'kelainan';
else $summary_type = 'kelainan'; // Riwayat (tidak ada is_normal) → kelainan
}
$id_code = $item['id_code'] ?? '';
$label = $item['label'] ?? '';
$value = $item['value'] ?? null;
$segment = $item['segment_name'] ?? ($item['_segment'] ?? null);
$value_sumber = $item['value_sumber'] ?? null;
$value_lama = $item['value_lama'] ?? null;
// -- Bangun LabelDisplay & Value --
//
// Pola universal:
// Fisik ber-segment → "Jantung : Apex"
// Fisik tanpa segment → "Deformitas" (label saja)
// XO (Penyakit Keluarga) → "Riwayat Penyakit Keluarga : Diabetes Melitus"
// Value = "Ayah, Ibu" (dari options.selected)
// XD (Imunisasi) → "Riwayat Imunisasi : Hepatitis A"
// XVS Riwayat ber-segment → "Riwayat Kebiasaan Hidup : Minum alkohol"
// XV Riwayat tanpa segment → "Riwayat Penyakit : Gastritis (maag)"
//
// Cara membedakan Riwayat vs Fisik: is_normal ada → Fisik, tidak ada → Riwayat
$title_clean = ucwords(strtolower(trim($tmpl_title)));
$is_riwayat_item = ($is_normal_field === null)
&& !in_array($type_form, ['XXV', 'XVV', 'TOOTH']);
if ($type_form === 'XO') {
// LabelDisplay = "Riwayat Penyakit Keluarga : Diabetes Melitus"
// Value = "Ayah, Ibu"
$label_display = $title_clean . ' : ' . $label;
if (!empty($item['options'])) {
$selected_opts = [];
foreach ($item['options'] as $opt) {
if (!empty($opt['selected'])) {
$selected_opts[] = $opt['label'];
}
}
if (count($selected_opts) > 0) {
$value = implode(', ', $selected_opts);
}
}
}
elseif ($is_riwayat_item && $segment) {
// Riwayat ber-segment: "Riwayat Kebiasaan Hidup : Minum alkohol"
$label_display = $title_clean . ' : ' . $segment;
}
elseif ($is_riwayat_item) {
// Riwayat tanpa segment: "Riwayat Penyakit : Gastritis (maag)"
$label_display = $title_clean . ' : ' . $label;
}
elseif ($segment) {
// Fisik ber-segment (is_normal ada): "Jantung : Apex"
$label_display = $title_clean . ' : ' . $segment;
}
else {
// Fisik tanpa segment: "Deformitas"
$label_display = $label;
}
return $this->insert_summary_row(
$order_header_id, $template_id, $tmpl_code, $tmpl_table,
$id_code, $label, $label_display,
$value, null, null, null,
$segment, $value_sumber, $value_lama,
$summary_type,
$user_id, $now
);
}
// ============================================================
// BUILD TANDA VITAL ROWS
// Dari JSON detail tanda vital, buat array item siap INSERT
// id_code: tanda_vital_5 → TD (sistole + diastole)
// tanda_vital_6 → Suhu
// tanda_vital_1 → Nadi
// tanda_vital_2 → Ritme nadi
// tanda_vital_3 → Laju nafas
// tanda_vital_4 → Pola nafas
// ============================================================
private function build_tanda_vital_rows($d)
{
$rows = [];
foreach ($d['details'] as $item) {
$code = $item['id_code'] ?? '';
// Tekanan Darah: value_x=sistole, value_y=diastole
if ($code === 'tanda_vital_5') {
$sistole = $item['value_x'] ?? null;
$diastole = $item['value_y'] ?? null;
$rows[] = [
'id_code' => 'tanda_vital_5',
'label' => 'Tekanan Darah',
'value' => $sistole,
'value2' => $diastole,
'value_numeric' => is_numeric($sistole) ? floatval($sistole) : null,
'value2_numeric' => is_numeric($diastole) ? floatval($diastole) : null,
];
}
// Suhu: value=angka, chx_x/chx_y → label ket (Febrile/Afebrile)
if ($code === 'tanda_vital_6') {
$suhu_val = $item['value'] ?? null;
if (!empty($item['chx_x'])) {
$ket = $item['label_x'] ?? null;
}
elseif (!empty($item['chx_y'])) {
$ket = $item['label_y'] ?? null;
}
else {
$ket = null;
}
$rows[] = [
'id_code' => 'tanda_vital_6',
'label' => 'Suhu',
'value' => $ket,
'value2' => $suhu_val,
'value_numeric' => null,
'value2_numeric' => is_numeric($suhu_val) ? floatval($suhu_val) : null,
];
}
// Denyut Nadi
if ($code === 'tanda_vital_1') {
$val = $item['value'] ?? null;
$rows[] = [
'id_code' => 'tanda_vital_1',
'label' => 'Denyut Nadi',
'value' => $val,
'value2' => null,
'value_numeric' => is_numeric($val) ? floatval($val) : null,
'value2_numeric' => null,
];
}
// Ritme Nadi: chx_y=Reguler, chx_x=Ireguler
if ($code === 'tanda_vital_2') {
if (!empty($item['chx_y'])) {
$ritme = $item['label_y'] ?? 'Reguler';
}
elseif (!empty($item['chx_x'])) {
$ritme = $item['label_x'] ?? 'Ireguler';
}
else {
$ritme = null;
}
$rows[] = [
'id_code' => 'tanda_vital_2',
'label' => 'Ritme Nadi',
'value' => $ritme,
'value2' => null,
'value_numeric' => null,
'value2_numeric' => null,
];
}
// Laju Pernafasan
if ($code === 'tanda_vital_3') {
$val = $item['value'] ?? null;
$rows[] = [
'id_code' => 'tanda_vital_3',
'label' => 'Laju Pernafasan',
'value' => $val,
'value2' => null,
'value_numeric' => is_numeric($val) ? floatval($val) : null,
'value2_numeric' => null,
];
}
// Pola Nafas
if ($code === 'tanda_vital_4') {
if (!empty($item['chx_y'])) {
$pola = $item['label_y'] ?? 'Normal';
}
elseif (!empty($item['chx_x'])) {
$pola = $item['label_x'] ?? 'Tidak normal';
}
else {
$pola = null;
}
$rows[] = [
'id_code' => 'tanda_vital_4',
'label' => 'Pola Nafas',
'value' => $pola,
'value2' => null,
'value_numeric' => null,
'value2_numeric' => null,
];
}
}
return $rows;
}
// ============================================================
// SAVE KELAINAN ITEM (legacy - masih dipakai oleh is_item_abnormal)
// ============================================================
private function save_kelainan_item(
$order_header_id, $template_id, $tmpl_code,
$tmpl_table, $type_form, $item, $user_id, $now
) {
if (!$this->is_item_abnormal($type_form, $item)) {
return false;
}
$id_code = $item['id_code'] ?? '';
$label = $item['label'] ?? '';
$value = $item['value'] ?? null;
$segment = $item['segment_name'] ?? ($item['_segment'] ?? null);
$value_sumber = $item['value_sumber'] ?? null;
$value_lama = $item['value_lama'] ?? null;
return $this->insert_summary_row(
$order_header_id, $template_id, $tmpl_code, $tmpl_table,
$id_code, $label,
$value, null, null, null,
$segment, $value_sumber, $value_lama,
'kelainan',
$user_id, $now
);
}
// ============================================================
// INSERT KE so_resultentry_fisik_summary
// Satu fungsi INSERT untuk semua tipe baris
// $label_display = teks siap tampil (Jantung : Apex, DM (Ayah), dst)
// ============================================================
private function insert_summary_row(
$order_header_id, $template_id, $tmpl_code, $tmpl_table,
$id_code, $label, $label_display,
$value, $value2, $value_numeric, $value2_numeric,
$segment, $value_sumber, $value_lama,
$summary_type,
$user_id, $now
) {
$sql = "INSERT INTO so_resultentry_fisik_summary (
So_ResultEntryFisikSummaryT_OrderHeaderID,
So_ResultEntryFisikSummaryFisikTemplateID,
So_ResultEntryFisikSummaryFisikTemplateCode,
So_ResultEntryFisikSummaryTableName,
So_ResultEntryFisikSummaryIdCode,
So_ResultEntryFisikSummaryLabel,
So_ResultEntryFisikSummaryLabelDisplay,
So_ResultEntryFisikSummarySegment,
So_ResultEntryFisikSummaryValue,
So_ResultEntryFisikSummaryValue2,
So_ResultEntryFisikSummaryValueNumeric,
So_ResultEntryFisikSummaryValue2Numeric,
So_ResultEntryFisikSummaryValueSumber,
So_ResultEntryFisikSummaryValueLama,
So_ResultEntryFisikSummaryType,
So_ResultEntryFisikSummaryIsActive,
So_ResultEntryFisikSummaryCreated,
So_ResultEntryFisikSummaryCreatedUserID,
So_ResultEntryFisikSummaryLastUpdated,
So_ResultEntryFisikSummaryLastUpdatedUserID
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'Y',?,?,?,?)";
$qry = $this->db_smartone->query($sql, [
$order_header_id,
$template_id,
$tmpl_code,
$tmpl_table,
$id_code,
$label,
$label_display,
$segment,
$value,
$value2,
$value_numeric,
$value2_numeric,
$value_sumber,
$value_lama,
$summary_type,
$now,
$user_id,
$now,
$user_id,
]);
return (bool)$qry;
}
// ============================================================
// IS ITEM ABNORMAL?
// Lihat comment lengkap di versi sebelumnya
// ============================================================
private function is_item_abnormal($type_form, $item)
{
$chx = !empty($item['chx']) || $item['chx'] === 1 || $item['chx'] === '1' || $item['chx'] === true;
$chx_x = !empty($item['chx_x']) || $item['chx_x'] === 1 || $item['chx_x'] === '1' || $item['chx_x'] === true;
$is_normal = isset($item['is_normal']) ? strtoupper($item['is_normal']) : null;
$skip_types = ['VXX+', 'VXX', 'V', 'T', 'XXVWL', 'XVS3R'];
if (in_array($type_form, $skip_types)) return false;
if ($type_form === 'XXV') return $chx_x;
if ($type_form === 'XVV') return $chx;
if ($type_form === 'TOOTH') return $chx;
if (in_array($type_form, ['XV', 'XVS', 'XVS-LXX', 'XD', 'XO'])) {
if ($is_normal !== null) {
return ($chx && $is_normal === 'N');
}
return $chx;
}
if ($type_form === 'X') return $chx;
return $chx;
}
// ============================================================
// GET TANDA VITAL
// Baca dari so_resultentry_fisik_summary (type='tanda_vital')
// Fallback ke so_resultentry_fisik_umum jika summary belum ada
// ============================================================
function get_tanda_vital($order_header_id, $langid = 1)
{
$empty = [
'td_sistole' => 'NaN',
'td_diastole' => 'NaN',
'td_nilai' => 'NaN',
'td_standart' => 'NaN',
'suhu' => 'NaN',
'suhu_ket' => 'NaN',
'nadi' => 'NaN',
'ritme_nadi' => 'NaN',
'laju_nafas' => 'NaN',
'pola_nafas' => 'NaN',
];
// -- Coba baca dari summary dulu (sudah diproses) --
$sql = "SELECT So_ResultEntryFisikSummaryIdCode as id_code,
So_ResultEntryFisikSummaryValue as val,
So_ResultEntryFisikSummaryValue2 as val2
FROM so_resultentry_fisik_summary
WHERE So_ResultEntryFisikSummaryT_OrderHeaderID = ?
AND So_ResultEntryFisikSummaryType = 'tanda_vital'
AND So_ResultEntryFisikSummaryIsActive = 'Y'";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if ($qry && count($qry->result_array()) > 0) {
$out = $empty;
foreach ($qry->result_array() as $r) {
switch ($r['id_code']) {
case 'tanda_vital_5':
$out['td_sistole'] = $r['val'] ?? '';
$out['td_diastole'] = $r['val2'] ?? '';
break;
case 'tanda_vital_6':
$out['suhu_ket'] = $this->translate_word($r['val'] ?? '', $langid);
$out['suhu'] = $r['val2'] ?? '';
break;
case 'tanda_vital_1':
$out['nadi'] = ($r['val'] !== null)
? $r['val'] . ' ' . $this->translate_word('x/menit', $langid)
: '';
break;
case 'tanda_vital_2':
$out['ritme_nadi'] = $this->translate_word($r['val'] ?? '', $langid);
break;
case 'tanda_vital_3':
$out['laju_nafas'] = ($r['val'] !== null)
? $r['val'] . ' ' . $this->translate_word('x/menit', $langid)
: '';
break;
case 'tanda_vital_4':
$out['pola_nafas'] = $this->translate_word($r['val'] ?? '', $langid);
break;
}
}
return $out;
}
// -- Fallback: baca dari so_resultentry_fisik_umum (JSON) --
$table = ($langid == 2) ? 'so_resultentry_fisik_umum_eng' : 'so_resultentry_fisik_umum';
$sql = "SELECT *
FROM {$table}
JOIN fisik_template
ON So_ResultEntryFisikUmumFisikTemplateID = FisikTemplateID
AND FisikTemplateIsActive = 'Y'
AND FisikTemplateTitle = 'TANDA VITAL'
WHERE So_ResultEntryFisikUmumT_OrderHeaderID = ?
AND So_ResultEntryFisikUmumIsActive = 'Y'
LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry) return $empty;
$rows = $qry->result_array();
if (count($rows) == 0) return $empty;
$d = json_decode($rows[0]['So_ResultEntryFisikUmumDetails'], true);
if (!$d || $d['title'] != 'TANDA VITAL') return $empty;
$out = $empty;
foreach ($d['details'] as $item) {
$code = $item['id_code'] ?? '';
if ($code === 'tanda_vital_5') {
$out['td_sistole'] = $item['value_x'] ?? '';
$out['td_diastole'] = $item['value_y'] ?? '';
$out['td_nilai'] = $item['value'] ?? '';
$out['td_standart'] = $item['standart'] ?? '';
}
if ($code === 'tanda_vital_6') {
$out['suhu'] = $item['value'] ?? '';
if (!empty($item['chx_x']))
$out['suhu_ket'] = $this->translate_word($item['label_x'] ?? '', $langid);
elseif (!empty($item['chx_y']))
$out['suhu_ket'] = $this->translate_word($item['label_y'] ?? '', $langid);
}
if ($code === 'tanda_vital_1') {
$v = $item['value'] ?? '';
$out['nadi'] = $v !== '' ? $v . ' ' . $this->translate_word('x/menit', $langid) : '';
}
if ($code === 'tanda_vital_2') {
if (!empty($item['chx_y']))
$out['ritme_nadi'] = $this->translate_word($item['label_y'] ?? 'Reguler', $langid);
elseif (!empty($item['chx_x']))
$out['ritme_nadi'] = $this->translate_word($item['label_x'] ?? 'Ireguler', $langid);
}
if ($code === 'tanda_vital_3') {
$v = $item['value'] ?? '';
$out['laju_nafas'] = $v !== '' ? $v . ' ' . $this->translate_word('x/menit', $langid) : '';
}
if ($code === 'tanda_vital_4') {
if (!empty($item['chx_y']))
$out['pola_nafas'] = $this->translate_word($item['label_y'] ?? 'Normal', $langid);
elseif (!empty($item['chx_x']))
$out['pola_nafas'] = $this->translate_word($item['label_x'] ?? 'Tidak normal', $langid);
}
}
return $out;
}
// ============================================================
// STATUS GIZI
// ============================================================
function get_status_gizi($order_header_id, $langid = 1)
{
$empty = [
'bb' => 'NaN',
'tb' => 'NaN',
'body_fat' => 'NaN',
'bmi' => 'NaN',
'bmi_classification' => 'NaN',
'bmi_standard' => 'NaN',
];
$sql = "SELECT *
FROM t_samplingso_additional_fisik_bbtb
WHERE T_SamplingAdditionalFisikBBTBT_OrderHeaderID = ?
LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry) return $empty;
$row = $qry->row_array();
if (!$row) return $empty;
$bb = $row['T_SamplingAdditionalFisikBBTBValueBB'];
$tb = $row['T_SamplingAdditionalFisikBBTBValueTB'];
$standart_bmi = $row['T_SamplingAdditionalFisikBBTBStandart'];
$bodyfat_raw = $row['T_SamplingAdditionalFisikBBTBBodyFat'];
if (intval($bb) == 0 && intval($tb) == 0) return $empty;
$bodyfat = ($bodyfat_raw == -1)
? $this->translate_word('Tidak dilakukan', $langid)
: $bodyfat_raw;
$get_bmi = $this->hitung_bmi($bb, $tb, $standart_bmi);
$bmi_class = $this->translate_word($get_bmi['class'], $langid);
return [
'bb' => $bb,
'tb' => $tb,
'body_fat' => $bodyfat,
'bmi' => $get_bmi['bmi'],
'bmi_classification' => $bmi_class,
'bmi_standard' => ucwords(str_replace('_', ' ', $standart_bmi)),
];
}
// ============================================================
// PERSEPSI WARNA
// ============================================================
function get_persepsi_warna($order_header_id, $langid = 1)
{
$empty = ['result' => 'NaN', 'angka' => ''];
$sql = "SELECT T_OrderDetailID FROM t_orderdetail
WHERE T_OrderDetailT_OrderHeaderID = ?
AND T_OrderDetailT_TestCode = '40111200'
AND T_OrderDetailIsActive = 'Y' LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry || count($qry->result_array()) == 0) return $empty;
$sql = "SELECT IFNULL(T_SamplingAdditionalFisikBWPWValue,'Z') as result,
IFNULL(T_SamplingAdditionalFisikBWPWVAngka,'') as angka
FROM t_samplingso_additional_fisik_bw
WHERE T_SamplingAdditionalFisikBWT_OrderHeaderID = ? LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry) return $empty;
$row = $qry->row_array();
if (!$row) return $empty;
if ($row['result'] == 'BW')
$result_text = 'Red Green Deficiency';
elseif ($row['result'] == 'N')
$result_text = 'Normal';
elseif ($row['result'] == 'X')
$result_text = $this->translate_word('Tidak dilakukan', $langid);
else
$result_text = $this->translate_word('Bahan Belum', $langid);
return ['result' => $result_text, 'angka' => $row['angka']];
}
// ============================================================
// VISUS
// ============================================================
function get_visus($order_header_id, $langid = 1)
{
$empty = [
'visus_kanan' => 'NaN', 'visus_kiri' => 'NaN',
'tk_od' => 'NaN', 'tk_os' => 'NaN',
'dk_od' => 'NaN', 'dk_os' => 'NaN',
'od_sph' => '-', 'od_cyl' => '-', 'od_x' => '-',
'os_sph' => '-', 'os_cyl' => '-', 'os_x' => '-',
'add' => '-', 'kelainan_kanan' => '', 'kelainan_kiri' => '',
];
$sql = "SELECT T_OrderDetailID FROM t_orderdetail
WHERE T_OrderDetailT_OrderHeaderID = ?
AND T_OrderDetailT_TestCode = '40111000'
AND T_OrderDetailIsActive = 'Y' LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry || count($qry->result_array()) == 0) return $empty;
$sql = "SELECT *, IFNULL(T_SamplingAdditionalFisikVisusID, 0) as visus_id
FROM t_samplingso_additional_fisik_visus
WHERE T_SamplingAdditionalFisikVisusT_OrderHeaderID = ? LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry) return $empty;
$row = $qry->row_array();
if (!$row || intval($row['visus_id']) == 0) {
return array_merge($empty, [
'visus_kanan' => $this->translate_word('Bahan belum', $langid),
'visus_kiri' => $this->translate_word('Bahan belum', $langid),
]);
}
$normal = ['20/20', '20/25', '6/6', '6/7.5', '6/7,5'];
$tk_od = $row['T_SamplingAdditionalFisikVisusTKODV'];
$tk_os = $row['T_SamplingAdditionalFisikVisusTKOSV'];
$dk_od = $row['T_SamplingAdditionalFisikVisusDKODV'];
$dk_os = $row['T_SamplingAdditionalFisikVisusDKOSV'];
$od_sph = $row['T_SamplingAdditionalFisikVisusODSPH'];
$od_cyl = $row['T_SamplingAdditionalFisikVisusODCYL'];
$od_x = $row['T_SamplingAdditionalFisikVisusODX'];
$os_sph = trim($row['T_SamplingAdditionalFisikVisusOSSPH']);
$os_cyl = $row['T_SamplingAdditionalFisikVisusOSCYL'];
$os_x = $row['T_SamplingAdditionalFisikVisusOSX'];
$od_add = $row['T_SamplingAdditionalFisikVisusADD'];
$is_valid = function ($v) {
$v = trim($v);
return $v !== '' && $v !== '-' && $v !== '--' && $v !== '/-'
&& $v !== '/' && $v !== '-/' && strtolower($v) !== 'plano'
&& strtolower($v) !== 'tidak terkoreksi'
&& strtolower($v) !== 'tidak dapat dinilai';
};
$kelainan_kanan = '';
if ($is_valid($od_sph)) $kelainan_kanan .= $od_sph . ' (' . $this->translate_word('Miopia', $langid) . ')';
if ($is_valid($od_cyl)) {
if ($kelainan_kanan != '') $kelainan_kanan .= ', ';
$kelainan_kanan .= 'Cyl ' . $od_cyl;
$kelainan_kanan .= $is_valid($od_x)
? ' axis ' . $od_x . ' (' . $this->translate_word('Astigmatismus', $langid) . ')'
: ' (' . $this->translate_word('Astigmatismus', $langid) . ')';
}
if ($is_valid($od_add)) {
if ($kelainan_kanan != '') $kelainan_kanan .= ', ';
$kelainan_kanan .= $od_add . ' (' . $this->translate_word('Presbiopia', $langid) . ')';
}
$kelainan_kiri = '';
if ($is_valid($os_sph)) $kelainan_kiri .= $os_sph . ' (' . $this->translate_word('Miopia', $langid) . ')';
if ($is_valid($os_cyl)) {
if ($kelainan_kiri != '') $kelainan_kiri .= ', ';
$kelainan_kiri .= 'Cyl ' . $os_cyl;
$kelainan_kiri .= $is_valid($os_x)
? ' axis ' . $os_x . ' (' . $this->translate_word('Astigmatismus', $langid) . ')'
: ' (' . $this->translate_word('Astigmatismus', $langid) . ')';
}
if ($is_valid($od_add)) {
if ($kelainan_kiri != '') $kelainan_kiri .= ', ';
$kelainan_kiri .= $od_add . ' (' . $this->translate_word('Presbiopia', $langid) . ')';
}
// Teks ringkas kanan
if ($tk_od == 'OFF' && $dk_od == 'OFF') {
$visus_kanan = $kelainan_kanan ?: 'Normal';
}
else {
$normal_kanan = (in_array($tk_od, $normal) || in_array($dk_od, $normal)) ? '(Normal)' : '';
$v_od = ($dk_od != '' && $dk_od != '-' && $dk_od != 'OFF') ? $dk_od : (($tk_od != '' && $tk_od != '-' && $tk_od != 'OFF') ? $tk_od : '20/20');
$st_od = ($dk_od != '' && $dk_od != '-' && $dk_od != 'OFF') ? $this->translate_word('dengan kacamata', $langid) : $this->translate_word('tanpa kacamata', $langid);
if (!in_array($v_od, $normal) && $kelainan_kanan == '') $kelainan_kanan = $this->translate_word('Miopia', $langid);
$pre = trim($v_od . ' ' . strtolower($st_od) . ' ' . $normal_kanan);
$visus_kanan = $kelainan_kanan == '' ? $pre : $pre . '; ' . $kelainan_kanan;
}
// Teks ringkas kiri
if ($tk_os == 'OFF' && $dk_os == 'OFF') {
$visus_kiri = $kelainan_kiri ?: 'Normal';
}
else {
$normal_kiri = (in_array($tk_os, $normal) || in_array($dk_os, $normal)) ? '(Normal)' : '';
$v_os = ($dk_os != '' && $dk_os != '-' && $dk_os != 'OFF') ? $dk_os : (($tk_os != '' && $tk_os != '-' && $tk_os != 'OFF') ? $tk_os : '20/20');
$st_os = ($dk_os != '' && $dk_os != '-' && $dk_os != 'OFF') ? $this->translate_word('dengan kacamata', $langid) : $this->translate_word('tanpa kacamata', $langid);
if (!in_array($v_os, $normal) && $kelainan_kiri == '') $kelainan_kiri = $this->translate_word('Miopia', $langid);
$pre = trim($v_os . ' ' . strtolower($st_os) . ' ' . $normal_kiri);
$visus_kiri = $kelainan_kiri == '' ? $pre : $pre . '; ' . $kelainan_kiri;
}
return [
'visus_kanan' => $visus_kanan, 'visus_kiri' => $visus_kiri,
'tk_od' => $tk_od ?: '-', 'tk_os' => $tk_os ?: '-',
'dk_od' => $dk_od ?: '-', 'dk_os' => $dk_os ?: '-',
'od_sph' => $od_sph ?: '-', 'od_cyl' => $od_cyl ?: '-', 'od_x' => $od_x ?: '-',
'os_sph' => $os_sph ?: '-', 'os_cyl' => $os_cyl ?: '-', 'os_x' => $os_x ?: '-',
'add' => $od_add ?: '-',
'kelainan_kanan' => $kelainan_kanan, 'kelainan_kiri' => $kelainan_kiri,
];
}
// ============================================================
// BODY FAT
// ============================================================
function get_body_fat($order_header_id, $langid = 1)
{
$sql = "SELECT T_SamplingAdditionalFisikBBTBBodyFat as body_fat
FROM t_samplingso_additional_fisik_bbtb
WHERE T_SamplingAdditionalFisikBBTBT_OrderHeaderID = ? LIMIT 1";
$qry = $this->db_smartone->query($sql, [$order_header_id]);
if (!$qry) return ['body_fat' => 'NaN', 'body_fat_display' => 'NaN'];
$row = $qry->row_array();
if (!$row) return ['body_fat' => 'NaN', 'body_fat_display' => 'NaN'];
$val = $row['body_fat'];
if ($val === null || $val === '') return ['body_fat' => 'NaN', 'body_fat_display' => 'NaN'];
if ($val == -1) return ['body_fat' => 'NaN', 'body_fat_display' => $this->translate_word('Tidak dilakukan', $langid)];
return ['body_fat' => $val, 'body_fat_display' => $val . '%'];
}
// ============================================================
// HITUNG BMI
// ============================================================
function hitung_bmi($bb, $tb, $standart_bmi)
{
if (floatval($tb) <= 0) return ['bmi' => 'NaN', 'class' => 'Undefined'];
$tb_m = floatval($tb) / 100;
$bmi = floatval($bb) / ($tb_m * $tb_m);
$bmi_v = number_format($bmi, 2);
$class = 'Undefined';
if ($standart_bmi === 'asia_pacific') {
if ($bmi_v < 18.5) $class = 'Underweight';
if ($bmi_v >= 18.5 && $bmi_v < 23) $class = 'Normal';
if ($bmi_v >= 23 && $bmi_v < 25) $class = 'Overweight';
if ($bmi_v >= 25 && $bmi_v < 30) $class = 'Obese I';
if ($bmi_v >= 30) $class = 'Obese II';
}
if ($standart_bmi === 'who') {
if ($bmi_v < 18.5) $class = 'Underweight';
if ($bmi_v >= 18.5 && $bmi_v < 25) $class = 'Normal';
if ($bmi_v >= 25 && $bmi_v < 30) $class = 'Overweight';
if ($bmi_v >= 30) $class = 'Obese';
}
if ($standart_bmi === 'kemenkes') {
if ($bmi_v < 18.5) $class = 'Underweight';
if ($bmi_v >= 18.5 && $bmi_v < 25.1) $class = 'Normal';
if ($bmi_v >= 25.1 && $bmi_v < 27) $class = 'Overweight';
if ($bmi_v >= 27) $class = 'Obese';
}
return ['bmi' => $bmi_v, 'class' => $class];
}
// ============================================================
// TRANSLATE
// ============================================================
function translate_word($word, $langid = 1)
{
if ($langid == 1) return $word;
$sql = "SELECT Translate_WordTo FROM translate_word
WHERE Translate_WordIsActive = 'Y' AND Translate_WordFrom = '{$word}' LIMIT 1";
$qry = $this->db_smartone->query($sql);
if (!$qry) return $word;
$rows = $qry->result_array();
return (count($rows) > 0) ? $rows[0]['Translate_WordTo'] : $word;
}
function translate_sentence($sentence, $langid = 1)
{
$words = preg_split('/[\s-]+/', $sentence);
return implode(' ', array_map(function ($w) use ($langid) {
return $this->translate_word($w, $langid);
}, $words));
}
function translate_multiline_text($text, $langid = 1)
{
$lines = explode("\n", $text);
return implode("\n", array_map(function ($line) use ($langid) {
return '- ' . $this->translate_sentence(ltrim($line, '- '), $langid);
}, $lines));
}
// ============================================================
// LOG ERROR
// ============================================================
function insert_log_error($log_sql, $params, $data = '')
{
$sql = "INSERT INTO one_lab_log.error_log(
ErrorLogCode, ErrorLogName, ErrorLogDescription,
ErrorLogData, ErrorLogOrderCreated
) VALUES(?,?,?,?,NOW())";
$query = $this->db_smartone->query($sql, [
$params[0], $params[1], $log_sql, json_encode($data),
]);
return (bool)$query;
}
}