db = $CI->load->database('onedev', true); } // ------------------------------------------------------------------------- // Public API // ------------------------------------------------------------------------- /** * Cari Nat_NormalValueID sesuai methode, test, sex, dan umur (dalam hari). * Urutan prioritas: * Type 1 — sex + age range * Type 2 — age range saja * Type 3 — sex saja * Type 4 — fallback */ public function get_normal_value_id($methodeID, $natTestID, $sexID, $ageInDay) { $methodeID = intval($methodeID); $natTestID = intval($natTestID); $sexID = intval($sexID); $ageInDay = intval($ageInDay); $min_days = $this->_age_sql('Nat_NormalValueMinAge'); $max_days = $this->_age_sql('Nat_NormalValueMaxAge'); $age_filter = " AND ( (Nat_NormalValueMinAgeInclusive = 'Y' AND ($min_days) <= $ageInDay) OR (Nat_NormalValueMinAgeInclusive = 'N' AND ($min_days) < $ageInDay) ) AND ( (Nat_NormalValueMaxAgeInclusive = 'Y' AND ($max_days) >= $ageInDay) OR (Nat_NormalValueMaxAgeInclusive = 'N' AND ($max_days) > $ageInDay) )"; $base = "FROM nat_normalvalue WHERE (Nat_NormalValueValidDate IS NULL OR Nat_NormalValueValidDate < NOW()) AND Nat_NormalValueIsActive = 'Y' AND Nat_NormalValueIsAbnormal = 'N' AND Nat_NormalValueNat_MethodeID = $methodeID AND Nat_NormalValueNat_TestID = $natTestID AND Nat_NormalValueNat_FlagID = 1"; $id = $this->_query_one("SELECT Nat_NormalValueID $base AND Nat_NormalValueNat_NormalValueTypeID = 1 AND Nat_NormalValueNat_SexID = $sexID $age_filter LIMIT 1"); if ($id) return $id; $id = $this->_query_one("SELECT Nat_NormalValueID $base AND Nat_NormalValueNat_NormalValueTypeID = 2 $age_filter LIMIT 1"); if ($id) return $id; $id = $this->_query_one("SELECT Nat_NormalValueID $base AND Nat_NormalValueNat_NormalValueTypeID = 3 AND Nat_NormalValueNat_SexID = $sexID LIMIT 1"); if ($id) return $id; $id = $this->_query_one("SELECT Nat_NormalValueID $base AND Nat_NormalValueNat_NormalValueTypeID = 4 LIMIT 1"); if ($id) return $id; return 0; } /** * Pengganti sp_sampling_check_normal_setting. * Mengembalikan array compat dengan call_multi_result_procedure(): * ok, message, result_sets[0]=summary, [1]=checks, [2]=branches, [3]=candidates */ public function check_setting_by_order_detail($orderDetailID) { $orderDetailID = intval($orderDetailID); $data = $this->_load_order_detail_data($orderDetailID); if ($data === null) { return [ 'ok' => false, 'message' => "T_OrderDetailID {$orderDetailID} tidak ditemukan atau tidak aktif", 'result_sets' => [], ]; } list($sexID, $dobStr, $orderDate, $ageInDay, $orderHeaderID, $patientID, $natTestID, $row) = $data; $methods = $this->_resolve_method_sources($natTestID); list($selID, $selName, $selSource) = $this->_pick_method($methods); $normalValueID = 0; if ($selID > 0 && $natTestID > 0 && $sexID > 0 && $ageInDay !== null) { $normalValueID = $this->get_normal_value_id($selID, $natTestID, $sexID, $ageInDay); } $summary = [[ 'T_OrderDetailID' => $orderDetailID, 'T_OrderHeaderID' => $orderHeaderID, 'M_PatientID' => $patientID, 'M_PatientM_SexID' => $sexID ?: null, 'M_PatientDOB' => $dobStr ?: null, 'T_OrderHeaderDate' => $row['order_date'], 'AgeInDay' => $ageInDay, 'T_TestID' => intval($row['t_test_id']), 'T_TestName' => $row['t_test_name'], 'Nat_TestID' => $natTestID ?: null, 'Nat_TestCode' => $row['nat_test_code'], 'Nat_TestName' => $row['nat_test_name'], 'T_OrderDetailNat_MethodeID'=> intval($row['order_detail_methode_id']), 'MethodeIDFromPriority' => $methods['priority_id'] ?: null, 'MethodeNameFromPriority' => $methods['priority_name'], 'MethodeIDFromInstrument' => $methods['instrument_id'] ?: null, 'MethodeNameFromInstrument' => $methods['instrument_name'], 'MethodeIDFromNormalValue' => $methods['normalvalue_id'] ?: null, 'MethodeNameFromNormalValue'=> $methods['normalvalue_name'], 'SelectedMethodeID' => $selID, 'SelectedMethodeName' => $selName, 'SelectedMethodeSource' => $selSource, 'FnSamplingGetNormalResult' => $normalValueID, ]]; $nvCount = 0; if ($natTestID > 0) { $r = $this->db->query( "SELECT COUNT(*) AS cnt FROM nat_normalvalue WHERE Nat_NormalValueNat_TestID = ? AND Nat_NormalValueNat_MethodeID = ? AND Nat_NormalValueIsActive = 'Y' AND Nat_NormalValueIsAbnormal = 'N'", [$natTestID, $selID ?: 0] )->row_array(); $nvCount = intval($r['cnt'] ?? 0); } $checks = [ $this->_chk('ORDER_DETAIL_ACTIVE', true, (string) $orderDetailID, 't_orderdetail aktif harus ditemukan'), $this->_chk('PATIENT_SEX', $sexID > 0, (string) ($sexID ?: 'NULL'), 'Dipakai pada type 1 dan type 3'), $this->_chk('PATIENT_DOB', $dobStr !== '', ($dobStr ?: 'NULL'), 'DOB dipakai untuk hitung AgeInDay'), $this->_chk('ORDER_DATE', !empty($row['order_date']), ($row['order_date'] ?: 'NULL'), 'Tanggal order dipakai untuk hitung AgeInDay'), $this->_chk('AGE_IN_DAY', $ageInDay !== null, ($ageInDay !== null ? (string) $ageInDay : 'NULL'), 'Dipakai pada type 1 dan type 2'), $this->_chk('NAT_TEST', $natTestID > 0, (string) ($natTestID ?: 'NULL'), 'Harus ada mapping T_TestNat_TestID'), $this->_chk('METHOD_PRIORITY', $methods['priority_id'] > 0, (string) ($methods['priority_id'] ?: 'NULL'), 'Sumber metode prioritas pertama'), $this->_chk('METHOD_INSTRUMENT', $methods['instrument_id'] > 0, (string) ($methods['instrument_id'] ?: 'NULL'), 'Fallback metode kedua'), $this->_chk('METHOD_NORMALVALUE', $methods['normalvalue_id'] > 0, (string) ($methods['normalvalue_id'] ?: 'NULL'), 'Fallback metode ketiga'), $this->_chk('SELECTED_METHOD', $selID > 0, (string) ($selID ?: 'NULL'), ($selSource ?: 'Tidak ada metode yang bisa dipakai')), $this->_chk('NORMALVALUE_TEST_METHOD_ACTIVE', $nvCount > 0, (string) $nvCount, 'Jumlah nat_normalvalue aktif non-abnormal untuk test + metode terpilih'), ['check_name' => 'FN_RESULT', 'check_status' => ($normalValueID > 0 ? 'OK' : 'NOT_FOUND'), 'check_value' => (string) $normalValueID, 'check_note' => 'Hasil akhir fn_sampling_get_normal'], ]; $branches = $selID > 0 ? $this->_query_branches($selID, $natTestID, $sexID, $ageInDay) : [['branch_name' => 'NO_SELECTED_METHOD', 'matched_count' => 0, 'first_normalvalue_id' => null]]; $candidates = $natTestID > 0 ? $this->_query_candidates($selID, $natTestID, $sexID, $ageInDay) : []; return [ 'ok' => true, 'message' => '', 'result_sets' => [$summary, $checks, $branches, $candidates], ]; } /** * Pengganti sp_sampling_fix_normal_by_orderdetail. * Mengembalikan array compat dengan call_multi_result_procedure(): * ok, message, result_sets[0]=result_row, [1]=order_detail_row */ public function fix_by_order_detail($orderDetailID) { $orderDetailID = intval($orderDetailID); $data = $this->_load_order_detail_data($orderDetailID); if ($data === null) { $errRow = [[ 'status' => 'ERR', 'message' => "T_OrderDetailID {$orderDetailID} tidak ditemukan atau tidak aktif", 'selected_methode_id' => null, 'selected_methode_name'=> null, 'selected_methode_source' => null, 'nat_normalvalue_id' => null, 'updated_rows' => 0, ]]; return ['ok' => true, 'message' => '', 'result_sets' => [$errRow, []]]; } list($sexID, $dobStr, $orderDate, $ageInDay, $orderHeaderID, $patientID, $natTestID, $row) = $data; $methods = $this->_resolve_method_sources($natTestID); list($selID, $selName, $selSource) = $this->_pick_method($methods); $normalValueID = 0; if ($selID > 0 && $natTestID > 0 && $sexID > 0 && $ageInDay !== null) { $normalValueID = $this->get_normal_value_id($selID, $natTestID, $sexID, $ageInDay); } if ($normalValueID > 0) { $this->db->query( "UPDATE t_orderdetail od JOIN nat_normalvalue nn ON nn.Nat_NormalValueID = ? SET od.T_OrderDetailNat_NormalValueID = nn.Nat_NormalValueID, od.T_OrderDetailNormalValueNote = nn.Nat_NormalValueNote, od.T_OrderDetailNormalValueDescription = nn.Nat_NormalValueDescription, od.T_OrderDetailMinValue = nn.Nat_NormalValueMinValue, od.T_OrderDetailMaxValue = nn.Nat_NormalValueMaxValue, od.T_OrderDetailMinValueInclusive = nn.Nat_NormalValueMinValueInclusive, od.T_OrderDetailMaxValueInclusive = nn.Nat_NormalValueMaxValueInclusive, od.T_OrderDetailNat_MethodeID = ?, od.T_OrderdetailNat_MethodeName = ? WHERE od.T_OrderDetailID = ? AND od.T_OrderDetailIsActive = 'Y'", [$normalValueID, $selID, $selName, $orderDetailID] ); $updatedRows = $this->db->affected_rows(); $resultRow = [[ 'status' => 'OK', 'message' => "Normal value berhasil diupdate untuk T_OrderDetailID {$orderDetailID}", 'selected_methode_id' => $selID, 'selected_methode_name'=> $selName, 'selected_methode_source' => $selSource, 'nat_normalvalue_id' => $normalValueID, 'updated_rows' => $updatedRows, ]]; } else { $resultRow = [[ 'status' => 'NOT_FOUND', 'message' => "Normal value tidak ditemukan untuk T_OrderDetailID {$orderDetailID}", 'selected_methode_id' => $selID, 'selected_methode_name'=> $selName, 'selected_methode_source' => $selSource, 'nat_normalvalue_id' => $normalValueID, 'updated_rows' => 0, ]]; } $odRow = $this->db->query( "SELECT T_OrderDetailID, T_OrderDetailNat_NormalValueID, T_OrderDetailNat_MethodeID, T_OrderdetailNat_MethodeName, T_OrderDetailNormalValueDescription, T_OrderDetailMinValue, T_OrderDetailMaxValue, T_OrderDetailMinValueInclusive, T_OrderDetailMaxValueInclusive FROM t_orderdetail WHERE T_OrderDetailID = ? LIMIT 1", [$orderDetailID] )->result_array(); return ['ok' => true, 'message' => '', 'result_sets' => [$resultRow, $odRow]]; } // ------------------------------------------------------------------------- // Private helpers // ------------------------------------------------------------------------- /** Inline pengganti fn_normal_get_age: konversi kolom usia ke hari di SQL. */ private function _age_sql($col) { return "CASE Nat_NormalValueAgeUnit WHEN 'HARI' THEN {$col} WHEN 'BULAN' THEN {$col} * 30 WHEN 'TAHUN' THEN {$col} * 365 ELSE {$col} END"; } /** * Ambil data dasar order detail termasuk DOB terenkripsi. * Return [sexID, dobStr, orderDate, ageInDay, orderHeaderID, patientID, natTestID, $row] * atau null jika tidak ditemukan. */ private function _load_order_detail_data($orderDetailID) { $row = $this->db->query( "SELECT od.T_OrderDetailT_OrderHeaderID AS order_header_id, oh.T_OrderHeaderM_PatientID AS patient_id, p.M_PatientM_SexID AS sex_id, p.M_PatientDOB_enc, p.M_PatientDOB, DATE(oh.T_OrderHeaderDate) AS order_date, od.T_OrderDetailT_TestID AS t_test_id, tt.T_TestName AS t_test_name, tt.T_TestNat_TestID AS nat_test_id, nt.Nat_TestCode AS nat_test_code, nt.Nat_TestName AS nat_test_name, od.T_OrderDetailNat_MethodeID AS order_detail_methode_id FROM t_orderdetail od JOIN t_orderheader oh ON oh.T_OrderHeaderID = od.T_OrderDetailT_OrderHeaderID JOIN m_patient p ON p.M_PatientID = oh.T_OrderHeaderM_PatientID LEFT JOIN t_test tt ON tt.T_TestID = od.T_OrderDetailT_TestID LEFT JOIN nat_test nt ON nt.Nat_TestID = tt.T_TestNat_TestID WHERE od.T_OrderDetailID = ? AND od.T_OrderDetailIsActive = 'Y' LIMIT 1", [$orderDetailID] )->row_array(); if (!$row) return null; $dobStr = ''; if (!empty($row['M_PatientDOB_enc'])) { $CI = &get_instance(); $CI->load->library('ibl_encryptor'); $dobStr = (string) ($CI->ibl_encryptor->decrypt($row['M_PatientDOB_enc']) ?? ''); } if ($dobStr === '' && !empty($row['M_PatientDOB'])) { $dobStr = $row['M_PatientDOB']; } $ageInDay = null; if ($dobStr !== '' && !empty($row['order_date'])) { try { $dob = new DateTime($dobStr); $oDate = new DateTime($row['order_date']); $ageInDay = (int) $oDate->diff($dob)->days; } catch (Exception $e) {} } return [ intval($row['sex_id']), $dobStr, $row['order_date'], $ageInDay, intval($row['order_header_id']), intval($row['patient_id']), intval($row['nat_test_id']), $row, ]; } /** Resolusi metode dari 3 sumber: priority → instrument → normalvalue. */ private function _resolve_method_sources($natTestID) { $r = $this->db->query( "SELECT mp.M_MethodePriorityNat_MethodeID, nm.Nat_MethodeName FROM m_methode_priority mp JOIN nat_methode nm ON nm.Nat_MethodeID = mp.M_MethodePriorityNat_MethodeID AND nm.Nat_MethodeIsActive = 'Y' JOIN m_instrumentmethode im ON im.M_InstrumentMethodeNat_MethodeID = nm.Nat_MethodeID AND im.M_InstrumentMethodeIsActive = 'Y' JOIN nat_instrument ni ON ni.Nat_InstrumentID = im.M_InstrumentMethodeNat_InstrumentID AND ni.Nat_InstrumentIsActive = 'Y' WHERE mp.M_MethodePriorityIsActive = 'Y' AND mp.M_MethodePriorityNat_TestID = ? AND mp.M_MethodePriorityM_DayOfWeekID = DAYOFWEEK(NOW()) ORDER BY mp.M_MethodePriorityNumber DESC LIMIT 1", [$natTestID] )->row_array(); $priorityID = intval($r['M_MethodePriorityNat_MethodeID'] ?? 0); $priorityName = $r['Nat_MethodeName'] ?? null; $r = $this->db->query( "SELECT im.M_InstrumentMethodeNat_MethodeID, nm.Nat_MethodeName FROM m_instrumentmethode im JOIN nat_methode nm ON nm.Nat_MethodeID = im.M_InstrumentMethodeNat_MethodeID AND nm.Nat_MethodeIsActive = 'Y' JOIN nat_instrument ni ON ni.Nat_InstrumentID = im.M_InstrumentMethodeNat_InstrumentID AND ni.Nat_InstrumentIsActive = 'Y' WHERE im.M_InstrumentMethodeNat_TestID = ? AND im.M_InstrumentMethodeIsActive = 'Y' ORDER BY im.M_InstrumentMethodePriority DESC LIMIT 1", [$natTestID] )->row_array(); $instrumentID = intval($r['M_InstrumentMethodeNat_MethodeID'] ?? 0); $instrumentName = $r['Nat_MethodeName'] ?? null; $r = $this->db->query( "SELECT nn.Nat_NormalValueNat_MethodeID, nm.Nat_MethodeName FROM nat_normalvalue nn JOIN nat_methode nm ON nm.Nat_MethodeID = nn.Nat_NormalValueNat_MethodeID AND nm.Nat_MethodeIsActive = 'Y' WHERE nn.Nat_NormalValueNat_TestID = ? AND nn.Nat_NormalValueIsActive = 'Y' ORDER BY nn.Nat_NormalValueID LIMIT 1", [$natTestID] )->row_array(); $normalID = intval($r['Nat_NormalValueNat_MethodeID'] ?? 0); $normalName = $r['Nat_MethodeName'] ?? null; return [ 'priority_id' => $priorityID, 'priority_name' => $priorityName, 'instrument_id' => $instrumentID, 'instrument_name'=> $instrumentName, 'normalvalue_id' => $normalID, 'normalvalue_name' => $normalName, ]; } /** Pilih metode terbaik dari hasil _resolve_method_sources(). */ private function _pick_method(array $methods) { if ($methods['priority_id'] > 0) { return [$methods['priority_id'], $methods['priority_name'], 'm_methode_priority']; } if ($methods['instrument_id'] > 0) { return [$methods['instrument_id'], $methods['instrument_name'], 'm_instrumentmethode']; } if ($methods['normalvalue_id'] > 0) { return [$methods['normalvalue_id'], $methods['normalvalue_name'], 'nat_normalvalue']; } return [0, null, null]; } /** Hitung matched_count per TYPE (1..4) untuk branch summary. */ private function _query_branches($methodeID, $natTestID, $sexID, $ageInDay) { $min = $this->_age_sql('Nat_NormalValueMinAge'); $max = $this->_age_sql('Nat_NormalValueMaxAge'); $base = "FROM nat_normalvalue WHERE (Nat_NormalValueValidDate IS NULL OR Nat_NormalValueValidDate < NOW()) AND Nat_NormalValueIsActive = 'Y' AND Nat_NormalValueIsAbnormal = 'N' AND Nat_NormalValueNat_FlagID = 1 AND Nat_NormalValueNat_MethodeID = $methodeID AND Nat_NormalValueNat_TestID = $natTestID"; $ageFilter = " AND ((Nat_NormalValueMinAgeInclusive='Y' AND ($min)<=$ageInDay) OR (Nat_NormalValueMinAgeInclusive='N' AND ($min)<$ageInDay)) AND ((Nat_NormalValueMaxAgeInclusive='Y' AND ($max)>=$ageInDay) OR (Nat_NormalValueMaxAgeInclusive='N' AND ($max)>$ageInDay))"; $types = [ ['TYPE_1', "AND Nat_NormalValueNat_NormalValueTypeID=1 AND Nat_NormalValueNat_SexID=$sexID $ageFilter"], ['TYPE_2', "AND Nat_NormalValueNat_NormalValueTypeID=2 $ageFilter"], ['TYPE_3', "AND Nat_NormalValueNat_NormalValueTypeID=3 AND Nat_NormalValueNat_SexID=$sexID"], ['TYPE_4', "AND Nat_NormalValueNat_NormalValueTypeID=4"], ]; $branches = []; foreach ($types as list($label, $filter)) { $r = $this->db->query("SELECT COUNT(*) AS cnt, MIN(Nat_NormalValueID) AS first_id $base $filter")->row_array(); $branches[] = [ 'branch_name' => $label, 'matched_count' => intval($r['cnt'] ?? 0), 'first_normalvalue_id'=> $r['first_id'] ?? null, ]; } return $branches; } /** Ambil semua kandidat nat_normalvalue dengan kolom diagnostik match/fail. */ private function _query_candidates($methodeID, $natTestID, $sexID, $ageInDay) { if (!$natTestID) return []; $min = $this->_age_sql('Nat_NormalValueMinAge'); $max = $this->_age_sql('Nat_NormalValueMaxAge'); $a = intval($ageInDay); $s = intval($sexID); $m = intval($methodeID); $matchMinAge = "( (Nat_NormalValueMinAgeInclusive='Y' AND ($min)<=$a) OR (Nat_NormalValueMinAgeInclusive='N' AND ($min)<$a))"; $matchMaxAge = "( (Nat_NormalValueMaxAgeInclusive='Y' AND ($max)>=$a) OR (Nat_NormalValueMaxAgeInclusive='N' AND ($max)>$a))"; $sql = "SELECT nn.Nat_NormalValueID, nn.Nat_NormalValueNat_TestID, nn.Nat_NormalValueNat_MethodeID, nm.Nat_MethodeName, nn.Nat_NormalValueNat_NormalValueTypeID, nn.Nat_NormalValueNat_SexID, nn.Nat_NormalValueValidDate, nn.Nat_NormalValueMinAge, nn.Nat_NormalValueMaxAge, nn.Nat_NormalValueAgeUnit, nn.Nat_NormalValueMinAgeInclusive, nn.Nat_NormalValueMaxAgeInclusive, ($min) AS MinAgeInDay, ($max) AS MaxAgeInDay, nn.Nat_NormalValueNat_FlagID, nn.Nat_NormalValueIsAbnormal, nn.Nat_NormalValueIsActive, IF((nn.Nat_NormalValueValidDate IS NULL OR nn.Nat_NormalValueValidDate < NOW()),'Y','N') AS MatchValidDate, IF(nn.Nat_NormalValueIsActive='Y','Y','N') AS MatchActive, IF(nn.Nat_NormalValueIsAbnormal='N','Y','N') AS MatchNotAbnormal, IF(nn.Nat_NormalValueNat_FlagID=1,'Y','N') AS MatchFlag, IF(nn.Nat_NormalValueNat_TestID=$natTestID,'Y','N') AS MatchNatTest, IF($m>0 AND nn.Nat_NormalValueNat_MethodeID=$m,'Y','N') AS MatchSelectedMethod, IF(nn.Nat_NormalValueNat_SexID=$s,'Y','N') AS MatchSex, IF($matchMinAge,'Y','N') AS MatchMinAge, IF($matchMaxAge,'Y','N') AS MatchMaxAge, CASE nn.Nat_NormalValueNat_NormalValueTypeID WHEN 1 THEN IF(nn.Nat_NormalValueNat_SexID=$s AND $matchMinAge AND $matchMaxAge,'Y','N') WHEN 2 THEN IF($matchMinAge AND $matchMaxAge,'Y','N') WHEN 3 THEN IF(nn.Nat_NormalValueNat_SexID=$s,'Y','N') WHEN 4 THEN 'Y' ELSE 'N' END AS MatchTypeRule, CONCAT_WS('; ', IF(NOT(nn.Nat_NormalValueValidDate IS NULL OR nn.Nat_NormalValueValidDate'Y','inactive',NULL), IF(nn.Nat_NormalValueIsAbnormal<>'N','abnormal_row',NULL), IF(nn.Nat_NormalValueNat_FlagID<>1,'flag_not_1',NULL), IF(nn.Nat_NormalValueNat_TestID<>$natTestID,'nat_test_mismatch',NULL), IF($m>0 AND nn.Nat_NormalValueNat_MethodeID<>$m,'methode_mismatch',NULL), IF(nn.Nat_NormalValueNat_NormalValueTypeID IN(1,3) AND nn.Nat_NormalValueNat_SexID<>$s,'sex_mismatch',NULL), IF(nn.Nat_NormalValueNat_NormalValueTypeID IN(1,2) AND NOT $matchMinAge,'age_below_min',NULL), IF(nn.Nat_NormalValueNat_NormalValueTypeID IN(1,2) AND NOT $matchMaxAge,'age_above_max',NULL) ) AS FailReason FROM nat_normalvalue nn LEFT JOIN nat_methode nm ON nm.Nat_MethodeID = nn.Nat_NormalValueNat_MethodeID WHERE nn.Nat_NormalValueNat_TestID = $natTestID AND nn.Nat_NormalValueIsActive = 'Y' AND nn.Nat_NormalValueIsAbnormal = 'N' ORDER BY CASE WHEN $m>0 AND nn.Nat_NormalValueNat_MethodeID=$m THEN 0 ELSE 1 END, nn.Nat_NormalValueNat_MethodeID, nn.Nat_NormalValueNat_NormalValueTypeID, nn.Nat_NormalValueID"; $qry = $this->db->query($sql); return $qry ? $qry->result_array() : []; } private function _chk($name, $ok, $value, $note) { return [ 'check_name' => $name, 'check_status' => $ok ? 'OK' : 'MISSING', 'check_value' => $value, 'check_note' => $note, ]; } private function _query_one($sql) { $qry = $this->db->query($sql); if (!$qry) return 0; $row = $qry->row_array(); return intval($row['Nat_NormalValueID'] ?? 0); } }