['step1' => 0, 'step2' => 0, 'step3' => 0, 'step4' => 0, 'step5' => 0, 'custom_total' => 0], 'clinics_wait' => [], 'trend_labels' => [], 'trend_wait' => [], 'trend_pt' => [], 'sql_logs' => [], 'debug_enabled' => ($show_sql_log_btn === 'Y') ]; $addLog = function ($name, $sql, $error = null) use (&$response) { $response['sql_logs'][] = ['name' => $name, 'sql' => trim(preg_replace('/\s+/', ' ', $sql)), 'status' => $error ? 'Error' : 'Success', 'error' => $error]; }; // แปลงรหัสการนัดหมายเป็นข้อความให้อ่านง่าย $appoint_names = ['' => 'ทั้งหมด', '1' => 'มาตามนัด', '2' => 'มาไม่ตรงนัด', '3' => 'Walk in (ไม่นัด)']; $appoint_label = $appoint_names[$appoint] ?? 'ทั้งหมด'; $addLog("ตรวจสอบเงื่อนไขหลัก", "ค้นหา: $start_date ถึง $end_date | นัด: $appoint_label | คลินิก: $clinic_id | เวลา: $time_period | Lab: $lab_status | X-ray: $xray_status"); $dep_names = ['000' => 'ผู้ป่วยนอก (ทั่วไป)', '130' => 'อายุรกรรม', '102' => 'ศัลยกรรม', '149' => 'ระบบประสาท', '168' => 'กุมารเวชกรรม', '220' => 'สูติ-นรีเวช', '073' => 'ตา', '072' => 'กระดูก', '111' => 'จิตเวช', '139' => 'เบาหวาน', '140' => 'ความดัน', '154' => 'ไขมัน', '137' => 'ไต', '110' => 'หอบหืด', '009' => 'ฝากครรภ์', '227' => 'ทางเดินอาหารและตับ', '024' => 'วัณโรค', '225' => 'อายุรกรรมโรคทางเดินหายใจและปอด', '017' => 'ทันตกรรม', '132' => 'คลินิกหัวใจ']; $thai_months = ['01' => 'ม.ค.', '02' => 'ก.พ.', '03' => 'มี.ค.', '04' => 'เม.ย.', '05' => 'พ.ค.', '06' => 'มิ.ย.', '07' => 'ก.ค.', '08' => 'ส.ค.', '09' => 'ก.ย.', '10' => 'ต.ค.', '11' => 'พ.ย.', '12' => 'ธ.ค.']; // 1. Mapping ฟิลด์สำหรับคำนวณและกรองเวลา (ทำก่อนสร้างเงื่อนไข) $allowed_cols = ['vsttime' => 'o.vsttime', 'service4' => 's.service4', 'service11' => 's.service11', 'service5' => 's.service5', 'service12' => 's.service12', 'service6' => 's.service6']; $col_from = isset($allowed_cols[$step_from]) ? $allowed_cols[$step_from] : "o.vsttime"; $col_to = isset($allowed_cols[$step_to]) ? $allowed_cols[$step_to] : "s.service6"; // --- สร้าง Dynamic WHERE Clause --- $where_cond = "s.vstdate BETWEEN :start AND :end "; $params = ['start' => $start_date, 'end' => $end_date]; // คงเงื่อนไขพื้นฐานเรื่องคนไข้ต้องผ่านช่องชำระเงิน $where_cond .= " AND s.vn IN (SELECT vn FROM rx_operator WHERE pay='Y') "; // กรองช่วงเวลา (อิงจากเวลาของจุดเริ่มต้น $col_from ที่ผู้ใช้เลือก) if ($time_period === 'morning') { $where_cond .= " AND $col_from BETWEEN '08:00:00' AND '12:00:00' "; } elseif ($time_period === 'afternoon') { $where_cond .= " AND $col_from BETWEEN '12:00:01' AND '16:00:00' "; } else { $where_cond .= " AND $col_from BETWEEN '08:00:00' AND '16:00:00' "; } // กรองคลินิก if ($clinic_id !== '') { $where_cond .= " AND o.main_dep = :clinic_id "; $params['clinic_id'] = $clinic_id; } // กรอง Lab / X-Ray if ($lab_status === '1') { $where_cond .= " AND s.vn IN (SELECT vn FROM lab_head) "; } elseif ($lab_status === '0') { $where_cond .= " AND s.vn NOT IN (SELECT vn FROM lab_head) "; } if ($xray_status === '1') { $where_cond .= " AND s.vn IN (SELECT vn FROM xray_head) "; } elseif ($xray_status === '0') { $where_cond .= " AND s.vn NOT IN (SELECT vn FROM xray_head) "; } // กรองการนัดหมาย พร้อมเก็บ Log แจกแจงว่าใช้คำสั่งย่อยอะไร $appoint_sql_log = "ไม่ได้จำกัดเงื่อนไขการนัดหมาย (ค้นหาทั้งหมด)"; if ($appoint === '1') { $appoint_sql_log = "AND s.vn IN (SELECT visit_vn FROM oapp)"; $where_cond .= " $appoint_sql_log "; } elseif ($appoint === '2') { // ชุดคำสั่งใหม่สำหรับ "มาไม่ตรงนัด" (ใช้ตัวแปร :start และ :end เพื่อให้กรองตามวันที่ที่เลือกได้) $appoint_sql_log = "AND s.vn IN (SELECT o.vn FROM ovst o INNER JOIN oapp a ON a.visit_vn = o.vn WHERE o.vstdate BETWEEN :start AND :end AND o.vstdate = a.nextdate)"; $where_cond .= " $appoint_sql_log "; } elseif ($appoint === '3') { $appoint_sql_log = "AND NOT EXISTS (SELECT 1 FROM oapp WHERE visit_vn = s.vn)"; $where_cond .= " $appoint_sql_log "; } $addLog("ชุดคำสั่งย่อยเฉพาะ [การนัดหมาย: $appoint_label]", $appoint_sql_log); // ผูกคำสั่งหลัก (Base Query) $base_from = "FROM service_time s JOIN ovst o ON s.vn = o.vn WHERE $where_cond AND s.service3 IS NOT NULL AND s.service4 IS NOT NULL AND s.service5 IS NOT NULL AND s.service11 IS NOT NULL AND s.service12 IS NOT NULL AND s.service6 IS NOT NULL AND s.service11>=s.service4 AND s.service5>=s.service11 AND s.service12>=s.service5 AND s.service6>s.service12 AND s.service16>s.service7 AND dayofweek(s.vstdate) NOT IN (1,7)"; if ($pdo) { // 1. SQL: Flowchart (ตามเงื่อนไข) $sql_flow = "SELECT IFNULL(ROUND(AVG((time_to_sec(s.service4)-time_to_sec(o.vsttime))/60), 2), 0) as step1, IFNULL(ROUND(AVG((time_to_sec(s.service11)-time_to_sec(s.service4))/60), 2), 0) as step2, IFNULL(ROUND(AVG((time_to_sec(s.service5)-time_to_sec(s.service11))/60), 2), 0) as step3, IFNULL(ROUND(AVG((time_to_sec(s.service12)-time_to_sec(s.service5))/60), 2), 0) as step4, IFNULL(ROUND(AVG((time_to_sec(s.service6)-time_to_sec(s.service12))/60), 2), 0) as step5, IFNULL(ROUND(AVG((time_to_sec($col_to)-time_to_sec($col_from))/60), 2), 0) as custom_total $base_from " . ($clinic_id === '' ? "AND o.main_dep='000'" : ""); // ถ้าไม่เลือกคลินิก ให้ default เป็น 000 ก่อน try { $stmt = $pdo->prepare($sql_flow); $stmt->execute($params); if ($row = $stmt->fetch()) { $response['flowchart'] = ['step1' => (float)$row['step1'], 'step2' => (float)$row['step2'], 'step3' => (float)$row['step3'], 'step4' => (float)$row['step4'], 'step5' => (float)$row['step5'], 'custom_total' => (float)$row['custom_total']]; } $addLog("Flowchart ระยะเวลารอคอย [นัดหมาย: $appoint_label]", $sql_flow); } catch (\Exception $e) { $addLog("Flowchart ระยะเวลารอคอย [นัดหมาย: $appoint_label]", $sql_flow, $e->getMessage()); } // 2. SQL: Clinic Wait (ตามเงื่อนไข) $sql_clinics = "SELECT o.main_dep, IFNULL(ROUND(AVG((time_to_sec($col_to)-time_to_sec($col_from))/60), 2), 0) as start2finish $base_from AND o.main_dep IN ('000','130','102','149','168','220','073','072','111','139','140','154','137','110','009','227','024','225','017','132') GROUP BY o.main_dep"; try { $stmt = $pdo->prepare($sql_clinics); $stmt->execute($params); while ($row = $stmt->fetch()) { if (isset($dep_names[$row['main_dep']])) { $response['clinics_wait'][] = ['name' => $dep_names[$row['main_dep']], 'wait' => (float)$row['start2finish']]; } } usort($response['clinics_wait'], function ($a, $b) { return $b['wait'] <=> $a['wait']; }); $addLog("ระยะเวลารอคอยแต่ละคลินิก [นัดหมาย: $appoint_label]", $sql_clinics); } catch (\Exception $e) { $addLog("ระยะเวลารอคอยแต่ละคลินิก [นัดหมาย: $appoint_label]", $sql_clinics, $e->getMessage()); } // 3. SQL: Trend Chart (ตามเงื่อนไข) $sql_trend = "SELECT DATE_FORMAT(s.vstdate, '%Y-%m') as ym, IFNULL(ROUND(AVG((time_to_sec($col_to)-time_to_sec($col_from))/60), 2), 0) as avg_wait, COUNT(DISTINCT s.vn) as pt_count $base_from " . ($clinic_id === '' ? "AND o.main_dep='000'" : "") . " GROUP BY ym ORDER BY ym ASC"; try { $stmt = $pdo->prepare($sql_trend); $stmt->execute($params); while ($row = $stmt->fetch()) { $parts = explode('-', $row['ym']); if (count($parts) == 2) { $y_th = (int)$parts[0] + 543; $response['trend_labels'][] = $thai_months[$parts[1]] . ' ' . substr($y_th, 2, 2); $response['trend_wait'][] = (float)$row['avg_wait']; $response['trend_pt'][] = (int)$row['pt_count']; } } $addLog("กราฟแนวโน้มระยะเวลารอคอย [นัดหมาย: $appoint_label]", $sql_trend); } catch (\Exception $e) { $addLog("กราฟแนวโน้มระยะเวลารอคอย [นัดหมาย: $appoint_label]", $sql_trend, $e->getMessage()); } } echo "---AJAX_OPD_COND_START---"; echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE); echo "---AJAX_OPD_COND_END---"; exit; } // ========================================================================= // 2. ส่วนประมวลผล AJAX ดึงข้อมูล "ภาพรวม OPD ทั่วไป" (ทำงานเมื่อกดค้นหาหน้าอื่นๆ) // ========================================================================= if ($isOpdAjax) { while (ob_get_level()) { ob_end_clean(); } header('Content-Type: application/json; charset=utf-8'); $current_month = (int)date('m'); $current_year = (int)date('Y'); $current_fy = ($current_month >= 10) ? $current_year + 1 : $current_year; $filter_type = $_GET['filter_type'] ?? 'fiscal'; $selected_fy = isset($_GET['fiscal_year']) ? (int)$_GET['fiscal_year'] : $current_fy; if ($selected_fy > 2500) { $selected_fy -= 543; } if ($filter_type === 'fiscal') { $start_date = ($selected_fy - 1) . '-10-01'; $end_date = $selected_fy . '-09-30'; } else { $start_date = $_GET['start_date'] ?? date('Y-m-01'); $end_date = $_GET['end_date'] ?? date('Y-m-d'); $s_parts = explode('-', $start_date); if (count($s_parts) === 3 && (int)$s_parts[0] > 2500) { $s_parts[0] = (int)$s_parts[0] - 543; $start_date = implode('-', $s_parts); } $e_parts = explode('-', $end_date); if (count($e_parts) === 3 && (int)$e_parts[0] > 2500) { $e_parts[0] = (int)$e_parts[0] - 543; $end_date = implode('-', $e_parts); } } $dep_names = ['000' => 'ผู้ป่วยนอก (ทั่วไป)', '130' => 'อายุรกรรม', '102' => 'ศัลยกรรม', '149' => 'ระบบประสาท', '168' => 'กุมารเวชกรรม', '220' => 'สูติ-นรีเวช', '073' => 'ตา', '072' => 'กระดูก', '111' => 'จิตเวช', '139' => 'เบาหวาน', '140' => 'ความดัน', '154' => 'ไขมัน', '137' => 'ไต', '110' => 'หอบหืด', '009' => 'ฝากครรภ์', '227' => 'ทางเดินอาหารและตับ', '024' => 'วัณโรค', '225' => 'โรคทางเดินหายใจและปอด', '017' => 'ทันตกรรม', '132' => 'หัวใจ']; $response = [ 'flowchart' => ['step1' => 0, 'step2' => 0, 'step3' => 0, 'step4' => 0, 'step5' => 0, 'total' => 0], 'trend_labels' => [], 'trend_wait' => [], 'trend_pt' => [], 'clinics_wait' => [], 'telemed_total' => 0, 'telemed_clinics' => [], 'home_delivery_total' => 0, 'home_delivery_trend' => ['labels' => [], 'data' => []], 'refer_in_total' => 0, 'refer_out_total' => 0, 'refer_back_total' => 0, 'refer_in_dest' => ['labels' => [], 'data' => []], 'refer_out_dest' => ['labels' => [], 'data' => []], 'refer_in_top10' => [], 'refer_out_top10' => [], 'doc_time_overall' => 0, 'doc_time_clinics' => [], 'sql_logs' => [], 'debug_enabled' => ($show_sql_log_btn === 'Y') ]; $addLog = function ($name, $sql, $error = null) use (&$response) { $response['sql_logs'][] = ['name' => $name, 'sql' => trim(preg_replace('/\s+/', ' ', $sql)), 'status' => $error ? 'Error' : 'Success', 'error' => $error]; }; $addLog('ระบบตรวจสอบช่วงวันที่ (Date Verification)', "ค้นหาข้อมูลตั้งแต่วันที่: $start_date ถึง $end_date"); $to_utf8 = function ($str) { if (empty($str)) return 'ไม่ระบุ'; if (!mb_check_encoding($str, 'UTF-8')) return @iconv('TIS-620', 'UTF-8//IGNORE', $str); return $str; }; $thai_months = ['01' => 'ม.ค.', '02' => 'ก.พ.', '03' => 'มี.ค.', '04' => 'เม.ย.', '05' => 'พ.ค.', '06' => 'มิ.ย.', '07' => 'ก.ค.', '08' => 'ส.ค.', '09' => 'ก.ย.', '10' => 'ต.ค.', '11' => 'พ.ย.', '12' => 'ธ.ค.']; if ($pdo) { // SQL: Flowchart $sql1 = $sql_config['opd']['flowchart'] ?? "SELECT IFNULL(ROUND(AVG((time_to_sec(s.service4)-time_to_sec(o.vsttime))/60), 2), 0) as step1, IFNULL(ROUND(AVG((time_to_sec(s.service11)-time_to_sec(s.service4))/60), 2), 0) as step2, IFNULL(ROUND(AVG((time_to_sec(s.service5)-time_to_sec(s.service11))/60), 2), 0) as step3, IFNULL(ROUND(AVG((time_to_sec(s.service12)-time_to_sec(s.service5))/60), 2), 0) as step4, IFNULL(ROUND(AVG((time_to_sec(s.service6)-time_to_sec(s.service12))/60), 2), 0) as step5, IFNULL(ROUND(AVG((time_to_sec(s.service6)-time_to_sec(s.service4))/60), 2), 0) as total_wait FROM service_time s JOIN ovst o ON s.vn = o.vn WHERE s.vstdate BETWEEN :start AND :end AND EXISTS (SELECT 1 FROM rx_operator rx WHERE rx.vn = s.vn AND rx.pay='Y' AND rx.rx_time BETWEEN '08:00:00' AND '16:00:00') AND s.service3 IS NOT NULL AND s.service4 IS NOT NULL AND s.service5 IS NOT NULL AND s.service11 IS NOT NULL AND s.service12 IS NOT NULL AND s.service6 IS NOT NULL AND s.service11>=s.service4 AND s.service5>=s.service11 AND s.service12>=s.service5 AND s.service6>s.service12 AND s.service16>s.service7 AND dayofweek(s.vstdate) NOT IN (1,7) AND o.main_dep='000'"; try { $stmt = $pdo->prepare($sql1); $stmt->execute(['start' => $start_date, 'end' => $end_date]); if ($row = $stmt->fetch()) { $response['flowchart'] = ['step1' => (float)$row['step1'], 'step2' => (float)$row['step2'], 'step3' => (float)$row['step3'], 'step4' => (float)$row['step4'], 'step5' => (float)$row['step5'], 'total' => (float)$row['total_wait']]; } $addLog('Flowchart ระยะเวลารอคอย', $sql1); } catch (\Exception $e) { $addLog('Flowchart ระยะเวลารอคอย', $sql1, $e->getMessage()); } // SQL: Trend Chart $sql_trend = $sql_config['opd']['trend'] ?? "SELECT DATE_FORMAT(s.vstdate, '%Y-%m') as ym, IFNULL(ROUND(AVG((time_to_sec(s.service6)-time_to_sec(s.service4))/60), 2), 0) as avg_wait, COUNT(DISTINCT s.vn) as pt_count FROM service_time s JOIN ovst o ON s.vn = o.vn WHERE s.vstdate BETWEEN :start AND :end AND EXISTS (SELECT 1 FROM rx_operator rx WHERE rx.vn = s.vn AND rx.pay='Y' AND rx.rx_time BETWEEN '08:00:00' AND '16:00:00') AND s.service3 IS NOT NULL AND s.service4 IS NOT NULL AND s.service5 IS NOT NULL AND s.service11 IS NOT NULL AND s.service12 IS NOT NULL AND s.service6 IS NOT NULL AND s.service11>=s.service4 AND s.service5>=s.service11 AND s.service12>=s.service5 AND s.service6>s.service12 AND s.service16>s.service7 AND dayofweek(s.vstdate) NOT IN (1,7) AND o.main_dep='000' GROUP BY ym ORDER BY ym ASC"; try { $stmt = $pdo->prepare($sql_trend); $stmt->execute(['start' => $start_date, 'end' => $end_date]); while ($row = $stmt->fetch()) { $parts = explode('-', $row['ym']); if (count($parts) == 2) { $y_th = (int)$parts[0] + 543; $response['trend_labels'][] = $thai_months[$parts[1]] . ' ' . substr($y_th, 2, 2); $response['trend_wait'][] = (float)$row['avg_wait']; $response['trend_pt'][] = (int)$row['pt_count']; } } $addLog('กราฟแนวโน้มระยะเวลารอคอย', $sql_trend); } catch (\Exception $e) { $addLog('กราฟแนวโน้มระยะเวลารอคอย', $sql_trend, $e->getMessage()); } // SQL: Clinic Wait $sql_clinics = $sql_config['opd']['clinics'] ?? "SELECT o.main_dep, IFNULL(ROUND(AVG((time_to_sec(s.service6)-time_to_sec(s.service4))/60), 2), 0) as start2finish FROM service_time s JOIN ovst o ON s.vn = o.vn WHERE s.vstdate BETWEEN :start AND :end AND EXISTS (SELECT 1 FROM rx_operator rx WHERE rx.vn = s.vn AND rx.pay='Y' AND rx.rx_time BETWEEN '08:00:00' AND '16:00:00') AND s.service3 IS NOT NULL AND s.service4 IS NOT NULL AND s.service5 IS NOT NULL AND s.service11 IS NOT NULL AND s.service12 IS NOT NULL AND s.service6 IS NOT NULL AND s.service11>=s.service4 AND s.service5>=s.service11 AND s.service12>=s.service5 AND s.service6>s.service12 AND s.service16>s.service7 AND dayofweek(s.vstdate) NOT IN (1,7) AND o.main_dep IN ('000','130','102','149','168','220','073','072','111','139','140','154','137','110','009','227','024','225','017','132') GROUP BY o.main_dep"; try { $stmt = $pdo->prepare($sql_clinics); $stmt->execute(['start' => $start_date, 'end' => $end_date]); $clinics_data = []; while ($row = $stmt->fetch()) { if (isset($dep_names[$row['main_dep']])) { $clinics_data[] = ['dep' => $row['main_dep'], 'name' => $dep_names[$row['main_dep']], 'wait' => (float)$row['start2finish']]; } } usort($clinics_data, function ($a, $b) { return $b['wait'] <=> $a['wait']; }); $response['clinics_wait'] = $clinics_data; $addLog('ระยะเวลารอคอยแต่ละคลินิกเฉลี่ย', $sql_clinics); } catch (\Exception $e) { $addLog('ระยะเวลารอคอยแต่ละคลินิกเฉลี่ย', $sql_clinics, $e->getMessage()); } // SQL: Telemedicine $sql_tele1 = $sql_config['opd']['tele_total'] ?? "SELECT count(vn) FROM ovst WHERE ovstist='15' AND vstdate BETWEEN :start AND :end"; $sql_tele2 = $sql_config['opd']['tele_clinics'] ?? "SELECT main_dep, count(vn) as cnt FROM ovst WHERE ovstist='15' AND vstdate BETWEEN :start AND :end AND main_dep IN ('000','130','102','149','168','220','073','072','111','139','140','154','137','110','009','227','024','225','017','132') GROUP BY main_dep ORDER BY cnt DESC LIMIT 10"; try { $stmt = $pdo->prepare($sql_tele1); $stmt->execute(['start' => $start_date, 'end' => $end_date]); $response['telemed_total'] = (int)$stmt->fetchColumn(); $addLog('ยอดรวม Telemedicine', $sql_tele1); $stmt2 = $pdo->prepare($sql_tele2); $stmt2->execute(['start' => $start_date, 'end' => $end_date]); $tele_clinics = []; while ($r = $stmt2->fetch()) { $dep_code = $r['main_dep']; $name = isset($dep_names[$dep_code]) ? $dep_names[$dep_code] : 'รหัส: ' . $dep_code; $tele_clinics[] = ['name' => $name, 'val' => (int)$r['cnt']]; } $response['telemed_clinics'] = $tele_clinics; $addLog('กราฟ 10 อันดับคลินิกให้บริการ Telemedicine', $sql_tele2); } catch (\Exception $e) { $addLog('Telemedicine', $sql_tele1 . ' | ' . $sql_tele2, $e->getMessage()); } // SQL: ยอดรวม OPD Refill (การส่งยาที่บ้าน) $sql_refill = $sql_config['opd']['refill_total'] ?? "SELECT count(vn) FROM ovst WHERE vstdate BETWEEN :start1 AND :end1 AND main_dep='256' OR vn IN(SELECT vn FROM opitemrece WHERE icode IN('3162977','3164210','3163020') AND vstdate BETWEEN :start2 AND :end2)"; try { $stmt = $pdo->prepare($sql_refill); $stmt->execute(['start1' => $start_date, 'end1' => $end_date, 'start2' => $start_date, 'end2' => $end_date]); $response['home_delivery_total'] = (int)$stmt->fetchColumn(); $addLog('ยอดรวมการส่งยาที่บ้าน OPD Refill', $sql_refill); } catch (\Exception $e) { $addLog('ยอดรวมการส่งยาที่บ้าน OPD Refill', $sql_refill, $e->getMessage()); } // SQL: กราฟ OPD Refill Trend (รายเดือน) $sql_refill_trend = $sql_config['opd']['refill_trend'] ?? "SELECT DATE_FORMAT(vstdate, '%Y-%m') as ym, count(vn) as cnt FROM ovst WHERE vstdate BETWEEN :start1 AND :end1 AND main_dep='256' OR vn IN(SELECT vn FROM opitemrece WHERE icode IN('3162977','3164210','3163020') AND vstdate BETWEEN :start2 AND :end2) AND vstdate BETWEEN :start3 AND :end3 GROUP BY ym ORDER BY ym ASC"; try { $stmt = $pdo->prepare($sql_refill_trend); $stmt->execute(['start1' => $start_date, 'end1' => $end_date, 'start2' => $start_date, 'end2' => $end_date, 'start3' => $start_date, 'end3' => $end_date]); while ($r = $stmt->fetch()) { $parts = explode('-', $r['ym']); if (count($parts) == 2) { $y_th = (int)$parts[0] + 543; $response['home_delivery_trend']['labels'][] = $thai_months[$parts[1]] . ' ' . substr($y_th, 2, 2); $response['home_delivery_trend']['data'][] = (int)$r['cnt']; } } $addLog('กราฟการส่งยาที่บ้าน OPD Refill (รายเดือน)', $sql_refill_trend); } catch (\Exception $e) { $addLog('กราฟการส่งยาที่บ้าน OPD Refill (รายเดือน)', $sql_refill_trend, $e->getMessage()); } // SQL: Refer $sql_ref_in1 = $sql_config['opd']['refer_in'] ?? "SELECT count(vn) FROM referin WHERE refer_hospcode IN ('09192','11359') AND refer_date BETWEEN :start AND :end"; $sql_ref_out1 = $sql_config['opd']['refer_out'] ?? "SELECT count(vn) FROM referout WHERE refer_hospcode IN ('10681') AND refer_date BETWEEN :start AND :end"; $sql_ref_back = $sql_config['opd']['refer_back'] ?? "SELECT count(distinct b.vn) FROM universal_head b WHERE b.universal_form_id ='124' AND b.vn IN(SELECT an FROM ipt WHERE dchdate BETWEEN :start AND :end)"; $sql_ref_in_dest = $sql_config['opd']['refer_in_dest'] ?? "SELECT count(a.vn) as a, h.name FROM referin a LEFT JOIN hospcode h ON a.refer_hospcode = h.hospcode WHERE a.refer_date BETWEEN :start AND :end AND a.refer_hospcode IS NOT NULL AND a.refer_hospcode NOT IN ('') GROUP BY a.refer_hospcode, h.name ORDER BY a DESC LIMIT 5"; $sql_ref_out_dest = $sql_config['opd']['refer_out_dest'] ?? "SELECT count(distinct a.vn) as vn, h.name FROM referout a LEFT JOIN hospcode h ON a.refer_hospcode = h.hospcode WHERE a.refer_date BETWEEN :start AND :end GROUP BY a.refer_hospcode, h.name ORDER BY vn DESC LIMIT 5"; $sql_ref_in_top = $sql_config['opd']['refer_in_top'] ?? "SELECT count(a.icd10) as c, b.name FROM referin a LEFT JOIN icd101 b ON a.icd10=b.code WHERE a.refer_date BETWEEN :start AND :end AND a.icd10 !='' AND a.icd10 !='U000' GROUP BY a.icd10, b.name ORDER BY c DESC LIMIT 10"; $sql_ref_out_top = $sql_config['opd']['refer_out_top'] ?? "SELECT count(distinct d.vn) as c, c_icd.name FROM referout d LEFT JOIN ovstdiag b ON d.vn=b.vn LEFT JOIN ovst o ON d.vn=o.vn LEFT JOIN icd101 c_icd ON b.icd10=c_icd.code WHERE d.refer_date BETWEEN :start AND :end AND o.an IS NULL AND b.diagtype='1' GROUP BY b.icd10, c_icd.name ORDER BY c DESC LIMIT 10"; try { $stmt = $pdo->prepare($sql_ref_in1); $stmt->execute(['start' => $start_date, 'end' => $end_date]); $response['refer_in_total'] = (int)$stmt->fetchColumn(); $addLog('ยอดรวมรับ Refer In', $sql_ref_in1); $stmt = $pdo->prepare($sql_ref_out1); $stmt->execute(['start' => $start_date, 'end' => $end_date]); $response['refer_out_total'] = (int)$stmt->fetchColumn(); $addLog('ยอดรวมส่ง Refer Out', $sql_ref_out1); $stmt = $pdo->prepare($sql_ref_back); $stmt->execute(['start' => $start_date, 'end' => $end_date]); $response['refer_back_total'] = (int)$stmt->fetchColumn(); $addLog('ส่งกลับ (Refer Back)', $sql_ref_back); $stmt = $pdo->prepare($sql_ref_in_dest); $stmt->execute(['start' => $start_date, 'end' => $end_date]); while ($r = $stmt->fetch()) { $response['refer_in_dest']['labels'][] = $to_utf8($r['name']) ?: 'ไม่ระบุ'; $response['refer_in_dest']['data'][] = (int)$r['a']; } $addLog('กราฟ Refer In แยก รพ.ต้นทาง', $sql_ref_in_dest); $stmt = $pdo->prepare($sql_ref_out_dest); $stmt->execute(['start' => $start_date, 'end' => $end_date]); while ($r = $stmt->fetch()) { $response['refer_out_dest']['labels'][] = $to_utf8($r['name']) ?: 'ไม่ระบุ'; $response['refer_out_dest']['data'][] = (int)$r['vn']; } $addLog('กราฟ Refer Out แยก รพ.ปลายทาง', $sql_ref_out_dest); $stmt = $pdo->prepare($sql_ref_in_top); $stmt->execute(['start' => $start_date, 'end' => $end_date]); while ($r = $stmt->fetch()) { $response['refer_in_top10'][] = ['name' => $to_utf8($r['name']) ?: 'ไม่ระบุ', 'val' => (int)$r['c']]; } $addLog('10 อันดับโรค รับ Refer In', $sql_ref_in_top); $stmt = $pdo->prepare($sql_ref_out_top); $stmt->execute(['start' => $start_date, 'end' => $end_date]); while ($r = $stmt->fetch()) { $response['refer_out_top10'][] = ['name' => $to_utf8($r['name']) ?: 'ไม่ระบุ', 'val' => (int)$r['c']]; } $addLog('10 อันดับโรค ส่ง Refer Out', $sql_ref_out_top); } catch (\Exception $e) { $addLog('การส่งต่อ (Refer)', 'หลายคำสั่ง', $e->getMessage()); } // SQL: Doctor Time $sql_doc = $sql_config['opd']['doc_time'] ?? "SELECT x.main_dep, COUNT(*) AS total_days, SUM(CASE WHEN x.first_service < '09:00:00' THEN 1 ELSE 0 END) AS before_9am_days FROM (SELECT v.vstdate, d.name, o.main_dep, MIN(s.service5) AS first_service FROM vn_stat v JOIN ovst o ON v.vn=o.vn JOIN service_time s ON v.vn=s.vn JOIN doctor d ON v.dx_doctor=d.code WHERE v.vstdate BETWEEN :start AND :end AND v.dx_doctor <> '' AND s.service5 IS NOT NULL AND d.position_id='1' AND d.doctor_department_id='58' AND v.vstdate NOT IN (SELECT holiday_date FROM holiday) AND o.vn NOT IN (SELECT vn FROM er_regist) GROUP BY v.vstdate, d.name, o.main_dep) as x GROUP BY x.main_dep"; try { $stmt = $pdo->prepare($sql_doc); $stmt->execute(['start' => $start_date, 'end' => $end_date]); $doc_totals = 0; $doc_before9 = 0; $clinics_doc = []; while ($r = $stmt->fetch()) { $doc_totals += $r['total_days']; $doc_before9 += $r['before_9am_days']; $dep = $r['main_dep']; if (!isset($clinics_doc[$dep])) { $clinics_doc[$dep] = ['tot' => 0, 'b9' => 0]; } $clinics_doc[$dep]['tot'] += $r['total_days']; $clinics_doc[$dep]['b9'] += $r['before_9am_days']; } $response['doc_time_overall'] = $doc_totals > 0 ? (float)number_format(($doc_before9 / $doc_totals) * 100, 2) : 0; foreach ($clinics_doc as $dep => $val) { if (isset($dep_names[$dep])) { $pct = $val['tot'] > 0 ? ($val['b9'] / $val['tot']) * 100 : 0; $response['doc_time_clinics'][] = ['name' => $dep_names[$dep], 'pct' => (float)number_format($pct, 2)]; } } usort($response['doc_time_clinics'], function ($a, $b) { return $b['pct'] <=> $a['pct']; }); $addLog('สถิติเวลาลงตรวจของแพทย์', $sql_doc); } catch (\Exception $e) { $addLog('สถิติเวลาลงตรวจของแพทย์', $sql_doc, $e->getMessage()); } } echo "---AJAX_OPD_START---"; $json_output = @json_encode($response, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE); if (!$json_output) { echo json_encode(['error' => 'JSON Encode Error: ' . json_last_error_msg(), 'flowchart' => ['total' => 0]]); } else { echo $json_output; } echo "---AJAX_OPD_END---"; exit; } // ========================================================================= // 3. สร้างฟอร์มการกรองมาตรฐาน // ========================================================================= $c_m = (int)date('m'); $c_y = (int)date('Y'); $c_fy = ($c_m >= 10) ? $c_y + 1 : $c_y; $options_html = ''; for ($i = 0; $i <= 5; $i++) { $y = $c_fy - $i; $yth = $y + 543; $options_html .= ""; } $start_d = date('Y-m-01'); $end_d = date('Y-m-d'); $opd_filter_html = '
'; ?>