428 lines
19 KiB
PHP
428 lines
19 KiB
PHP
<?php
|
|
// api/jobs.php
|
|
require_once dirname(__DIR__) . '/config/config.php';
|
|
require_once dirname(__DIR__) . '/models/Patient.php';
|
|
require_once dirname(__DIR__) . '/models/Job.php';
|
|
require_once dirname(__DIR__) . '/config/LineBot.php';
|
|
require_once dirname(__DIR__) . '/config/Dispatcher.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
$action = $_REQUEST['action'] ?? '';
|
|
|
|
// Check for post_max_size violation (which clears $_POST and $_FILES)
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($_POST) && empty($_FILES) && isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) {
|
|
echo json_encode(['status' => 'error', 'message' => 'ไฟล์รูปภาพมีขนาดใหญ่เกินกว่าที่เซิร์ฟเวอร์จะรับได้ (เกินขีดจำกัด post_max_size) กรุณาลดขนาดไฟล์ก่อนอัปโหลด']);
|
|
exit;
|
|
}
|
|
|
|
// Need to be logged in (Except for public job creation)
|
|
if (!isset($_SESSION['user_id']) && $action !== 'create_public_job') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Unauthorized']);
|
|
exit;
|
|
}
|
|
|
|
if ($action === 'search_patient') {
|
|
$hn = $_GET['hn'] ?? '';
|
|
if (empty($hn)) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Missing HN']);
|
|
exit;
|
|
}
|
|
|
|
$patient = Patient::searchByHN($hn);
|
|
if ($patient) {
|
|
echo json_encode(['status' => 'success', 'data' => $patient]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Patient not found']);
|
|
}
|
|
} elseif ($action === 'create_job') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid method']);
|
|
exit;
|
|
}
|
|
|
|
// CSRF check
|
|
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid CSRF Token']);
|
|
exit;
|
|
}
|
|
|
|
$data = [
|
|
'patient_hn' => $_POST['patient_hn'] ?? '',
|
|
'patient_name' => $_POST['patient_name'] ?? '',
|
|
'from_department_id' => $_POST['from_department_id'] ?? '',
|
|
'to_department_id' => $_POST['to_department_id'] ?? '',
|
|
'equipment_type' => $_POST['equipment_type'] ?? 'wheelchair',
|
|
'priority' => $_POST['priority'] ?? 'normal',
|
|
'notes' => $_POST['notes'] ?? '',
|
|
'requester_id' => $_SESSION['user_id'],
|
|
'infection_control' => isset($_POST['infection_control']) ? 1 : 0,
|
|
'is_scheduled' => isset($_POST['is_scheduled']) ? (int)$_POST['is_scheduled'] : 0,
|
|
'scheduled_time' => $_POST['scheduled_time'] ?? null,
|
|
'is_round_trip' => isset($_POST['is_round_trip']) ? 1 : 0
|
|
];
|
|
|
|
if (empty($data['patient_hn']) || empty($data['from_department_id']) || empty($data['to_department_id'])) {
|
|
echo json_encode(['status' => 'error', 'message' => 'ข้อมูลไม่ครบถ้วน']);
|
|
exit;
|
|
}
|
|
|
|
$jobModel = new Job();
|
|
$job_number = $jobModel->create($data);
|
|
|
|
if ($job_number) {
|
|
require_once dirname(__DIR__) . '/models/Setting.php';
|
|
require_once dirname(__DIR__) . '/config/TelegramBot.php';
|
|
|
|
$settingModel = new Setting();
|
|
$auto_dispatch = $settingModel->get('auto_dispatch_enabled', '0');
|
|
|
|
$msg = "🚨 มีงานใหม่!\nหมายเลข: {$job_number}\nผู้ป่วย: {$data['patient_name']}\nอุปกรณ์: {$data['equipment_type']}";
|
|
|
|
// Trigger LINE Notification
|
|
$lineBot = new LineBot();
|
|
$lineBot->sendGroupMessage($msg, $job_number); // will return false if no token
|
|
|
|
// Trigger Telegram Notification
|
|
$telegramBot = new TelegramBot();
|
|
$telegramBot->sendGroupMessage($msg, $job_number);
|
|
|
|
// Smart Auto Dispatch (If enabled)
|
|
if ($auto_dispatch === '1') {
|
|
$dispatcher = new Dispatcher();
|
|
// Actually pass the job_number / ID to dispatcher
|
|
// Wait, Dispatcher takes job_id. job_number is just a string (e.g. JOB2024...).
|
|
// Let's assume jobModel->create() returns the inserted ID, or we need to find the ID.
|
|
// Oh, previously $job_number = $jobModel->create($data) actually returned job_number.
|
|
// Let's fetch the actual job_id to pass to autoDispatch.
|
|
$jobInfo = $jobModel->getByJobNumber($job_number);
|
|
if ($jobInfo) {
|
|
$dispatcher->autoDispatch($jobInfo['id']);
|
|
}
|
|
}
|
|
|
|
echo json_encode(['status' => 'success', 'job_number' => $job_number]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'เกิดข้อผิดพลาดในการบันทึกข้อมูล']);
|
|
}
|
|
} elseif ($action === 'create_public_job') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid method']);
|
|
exit;
|
|
}
|
|
|
|
// CSRF check
|
|
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid CSRF Token']);
|
|
exit;
|
|
}
|
|
|
|
// Assign to a default admin user ID or get the first available admin
|
|
$jobModel = new Job();
|
|
$db = Database::getInstance();
|
|
$stmt = $db->query("SELECT id FROM users WHERE role = 'admin' LIMIT 1");
|
|
$admin = $stmt->fetch();
|
|
$requester_id = $admin ? $admin['id'] : 1;
|
|
|
|
$to_department_id = $_POST['to_department_id'] ?? '';
|
|
$to_department_custom = null;
|
|
|
|
if ($to_department_id === 'custom') {
|
|
$to_department_id = null;
|
|
$to_department_custom = trim($_POST['to_department_custom'] ?? '');
|
|
}
|
|
|
|
$patient_name = trim($_POST['patient_name'] ?? '');
|
|
|
|
// Handle Image Upload with Compression
|
|
$patient_image_path = null;
|
|
if (isset($_FILES['patient_image']) && $_FILES['patient_image']['error'] === UPLOAD_ERR_OK) {
|
|
$uploadDir = dirname(__DIR__) . '/uploads/patients/';
|
|
if (!is_dir($uploadDir)) {
|
|
mkdir($uploadDir, 0777, true);
|
|
}
|
|
|
|
$fileExt = strtolower(pathinfo($_FILES['patient_image']['name'], PATHINFO_EXTENSION));
|
|
$allowedExt = ['jpg', 'jpeg', 'png', 'webp'];
|
|
|
|
if (in_array($fileExt, $allowedExt)) {
|
|
// Force save as JPG for better compression of photos
|
|
$fileName = uniqid('img_') . '_' . time() . '.jpg';
|
|
$destPath = $uploadDir . $fileName;
|
|
$sourcePath = $_FILES['patient_image']['tmp_name'];
|
|
|
|
// Image compression and resize logic (Max 800px, 75% quality)
|
|
$info = getimagesize($sourcePath);
|
|
if ($info !== false && function_exists('imagecreatefromjpeg')) {
|
|
$image = null;
|
|
if ($info['mime'] == 'image/jpeg') $image = imagecreatefromjpeg($sourcePath);
|
|
elseif ($info['mime'] == 'image/png') $image = imagecreatefrompng($sourcePath);
|
|
elseif ($info['mime'] == 'image/webp' && function_exists('imagecreatefromwebp')) $image = imagecreatefromwebp($sourcePath);
|
|
elseif ($info['mime'] == 'image/gif') $image = imagecreatefromgif($sourcePath);
|
|
|
|
if ($image !== null) {
|
|
$maxWidth = 800;
|
|
$width = $info[0];
|
|
$height = $info[1];
|
|
|
|
if ($width > $maxWidth || $height > $maxWidth) {
|
|
$ratio = $width / $height;
|
|
if ($ratio > 1) {
|
|
$newWidth = $maxWidth;
|
|
$newHeight = $maxWidth / $ratio;
|
|
} else {
|
|
$newWidth = $maxWidth * $ratio;
|
|
$newHeight = $maxWidth;
|
|
}
|
|
$newImage = imagecreatetruecolor($newWidth, $newHeight);
|
|
// Convert white background for transparent images
|
|
$white = imagecolorallocate($newImage, 255, 255, 255);
|
|
imagefill($newImage, 0, 0, $white);
|
|
|
|
imagecopyresampled($newImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
|
|
$image = $newImage;
|
|
}
|
|
|
|
// Save as JPEG
|
|
if (imagejpeg($image, $destPath, 75)) {
|
|
$patient_image_path = 'uploads/patients/' . $fileName;
|
|
}
|
|
imagedestroy($image);
|
|
} else {
|
|
if (move_uploaded_file($sourcePath, $destPath)) {
|
|
$patient_image_path = 'uploads/patients/' . $fileName;
|
|
}
|
|
}
|
|
} else {
|
|
// Fallback if getimagesize fails, GD missing, or extension is allowed (rare)
|
|
if (move_uploaded_file($sourcePath, $destPath)) {
|
|
$patient_image_path = 'uploads/patients/' . $fileName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (empty($patient_name) && empty($patient_image_path)) {
|
|
echo json_encode(['status' => 'error', 'message' => 'กรุณาระบุชื่อผู้ป่วยหรือถ่ายรูปอย่างน้อยหนึ่งอย่าง']);
|
|
exit;
|
|
}
|
|
|
|
if (empty($_POST['from_department_id']) || (empty($to_department_id) && empty($to_department_custom))) {
|
|
echo json_encode(['status' => 'error', 'message' => 'ข้อมูลจุดรับส่งไม่ครบถ้วน']);
|
|
exit;
|
|
}
|
|
|
|
$data = [
|
|
'patient_hn' => $_POST['patient_hn'] ?? '',
|
|
'patient_name' => $patient_name,
|
|
'patient_image' => $patient_image_path,
|
|
'from_department_id' => $_POST['from_department_id'] ?? '',
|
|
'to_department_id' => $to_department_id,
|
|
'to_department_custom' => $to_department_custom,
|
|
'equipment_type' => $_POST['equipment_type'] ?? 'wheelchair',
|
|
'priority' => $_POST['priority'] ?? 'normal',
|
|
'notes' => ($_POST['notes'] ?? '') . "\n[เรียกผ่าน QR Code จุดรับผู้ป่วย]",
|
|
'requester_id' => $requester_id,
|
|
'infection_control' => isset($_POST['infection_control']) ? 1 : 0,
|
|
'is_scheduled' => 0,
|
|
'scheduled_time' => null,
|
|
'is_round_trip' => isset($_POST['is_round_trip']) ? 1 : 0
|
|
];
|
|
|
|
try {
|
|
$job_number = $jobModel->create($data);
|
|
|
|
if ($job_number) {
|
|
require_once dirname(__DIR__) . '/models/Setting.php';
|
|
require_once dirname(__DIR__) . '/config/TelegramBot.php';
|
|
|
|
$settingModel = new Setting();
|
|
$auto_dispatch = $settingModel->get('auto_dispatch_enabled', '0');
|
|
|
|
$display_name = empty($data['patient_name']) ? 'ไม่ระบุชื่อ (ดูรูปในระบบ)' : $data['patient_name'];
|
|
$display_to = empty($data['to_department_id']) ? $data['to_department_custom'] : "แผนกปลายทาง (ID: {$data['to_department_id']})";
|
|
|
|
$msg = "🚨 แสกนเรียกจากจุดรับ!\nหมายเลข: {$job_number}\nผู้ป่วย: {$display_name}\nปลายทาง: {$display_to}\nอุปกรณ์: {$data['equipment_type']}";
|
|
|
|
// Trigger LINE Notification
|
|
$lineBot = new LineBot();
|
|
$lineBot->sendGroupMessage($msg, $job_number);
|
|
|
|
// Trigger Telegram Notification
|
|
$telegramBot = new TelegramBot();
|
|
$telegramBot->sendGroupMessage($msg, $job_number);
|
|
|
|
// Smart Auto Dispatch (If enabled)
|
|
if ($auto_dispatch === '1') {
|
|
$dispatcher = new Dispatcher();
|
|
$jobInfo = $jobModel->getByJobNumber($job_number);
|
|
if ($jobInfo) {
|
|
$dispatcher->autoDispatch($jobInfo['id']);
|
|
}
|
|
}
|
|
|
|
echo json_encode(['status' => 'success', 'job_number' => $job_number]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'เกิดข้อผิดพลาดในการบันทึกข้อมูล']);
|
|
}
|
|
} catch (Throwable $e) {
|
|
echo json_encode(['status' => 'error', 'message' => 'เกิดข้อผิดพลาด: ' . $e->getMessage() . ' (Line: ' . $e->getLine() . ')']);
|
|
}
|
|
} elseif ($action === 'save_signature') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid method']);
|
|
exit;
|
|
}
|
|
|
|
$job_id = $_POST['job_id'] ?? '';
|
|
$signature_data = $_POST['signature'] ?? '';
|
|
|
|
if (empty($job_id) || empty($signature_data)) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Missing data']);
|
|
exit;
|
|
}
|
|
|
|
// Decode Base64 image
|
|
list($type, $signature_data) = explode(';', $signature_data);
|
|
list(, $signature_data) = explode(',', $signature_data);
|
|
$signature_data = base64_decode($signature_data);
|
|
|
|
$filename = "sig_" . time() . "_" . uniqid() . ".png";
|
|
$filepath = dirname(__DIR__) . "/uploads/signatures/" . $filename;
|
|
|
|
if (file_put_contents($filepath, $signature_data)) {
|
|
// Update jobs table with signature_path and set status to completed
|
|
$jobModel = new Job();
|
|
// Since we have job_number from client, we need job_id
|
|
$jobInfo = $jobModel->getByJobNumber($job_id);
|
|
if ($jobInfo) {
|
|
$jobModel->updateStatus($jobInfo['id'], 'completed', $_SESSION['user_id']);
|
|
// Add a quick direct SQL to update signature path
|
|
$db = Database::getInstance();
|
|
$stmt = $db->prepare("UPDATE jobs SET notes = CONCAT(IFNULL(notes,''), '\n[Signature: ', :sig, ']') WHERE id = :id");
|
|
$stmt->execute([':sig' => $filename, ':id' => $jobInfo['id']]);
|
|
}
|
|
echo json_encode(['status' => 'success', 'file' => $filename]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Failed to save signature']);
|
|
}
|
|
} elseif ($action === 'get_active_job') {
|
|
$jobModel = new Job();
|
|
$activeJob = $jobModel->getActiveJobByStaff($_SESSION['user_id']);
|
|
|
|
if ($activeJob) {
|
|
require_once dirname(__DIR__) . '/models/Setting.php';
|
|
$settingModel = new Setting();
|
|
|
|
$equipments = json_decode($settingModel->get('equipment_types') ?: '[]', true);
|
|
$priorities = json_decode($settingModel->get('job_priorities') ?: '[]', true);
|
|
|
|
// Defaults
|
|
$activeJob['equipment_name'] = $activeJob['equipment_type'];
|
|
$activeJob['equipment_icon'] = 'fa-wheelchair';
|
|
$activeJob['priority_name'] = $activeJob['priority'];
|
|
$activeJob['priority_color'] = 'blue';
|
|
|
|
// Match equipment
|
|
foreach ($equipments as $eq) {
|
|
if ($eq['id'] === $activeJob['equipment_type']) {
|
|
$activeJob['equipment_name'] = $eq['name'];
|
|
$activeJob['equipment_icon'] = $eq['icon'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Match priority
|
|
foreach ($priorities as $pri) {
|
|
if ($pri['id'] === $activeJob['priority']) {
|
|
$activeJob['priority_name'] = $pri['name'];
|
|
$activeJob['priority_color'] = $pri['color'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
echo json_encode(['status' => 'success', 'data' => $activeJob]);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'No active job']);
|
|
}
|
|
} elseif ($action === 'update_status') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid method']);
|
|
exit;
|
|
}
|
|
|
|
$job_id = $_POST['job_id'] ?? '';
|
|
$new_status = $_POST['status'] ?? '';
|
|
|
|
if (empty($job_id) || empty($new_status)) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Missing data']);
|
|
exit;
|
|
}
|
|
|
|
$jobModel = new Job();
|
|
$jobInfo = $jobModel->getByJobNumber($job_id);
|
|
if ($jobInfo) {
|
|
if ($jobModel->updateStatus($jobInfo['id'], $new_status, $_SESSION['user_id'])) {
|
|
echo json_encode(['status' => 'success', 'message' => 'Status updated']);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Failed to update status']);
|
|
}
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Job not found']);
|
|
}
|
|
} elseif ($action === 'assign_job') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid method']);
|
|
exit;
|
|
}
|
|
|
|
$job_id = $_POST['job_id'] ?? '';
|
|
if (empty($job_id)) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Missing data']);
|
|
exit;
|
|
}
|
|
|
|
$jobModel = new Job();
|
|
$jobInfo = $jobModel->getByJobNumber($job_id);
|
|
if ($jobInfo && $jobInfo['status'] === 'pending') {
|
|
$db = Database::getInstance();
|
|
$stmt = $db->prepare("UPDATE jobs SET status = 'assigned', assigned_to = :user_id WHERE id = :id");
|
|
$stmt->bindParam(':user_id', $_SESSION['user_id']);
|
|
$stmt->bindParam(':id', $jobInfo['id']);
|
|
if ($stmt->execute()) {
|
|
echo json_encode(['status' => 'success', 'message' => 'รับงานสำเร็จ']);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'ไม่สามารถรับงานได้']);
|
|
}
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'งานนี้ถูกรับไปแล้วหรือไม่พบงาน']);
|
|
}
|
|
} elseif ($action === 'cancel_job') {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
echo json_encode(['status' => 'error', 'message' => 'Invalid method']);
|
|
exit;
|
|
}
|
|
|
|
$job_id = $_POST['job_id'] ?? '';
|
|
$reason = $_POST['reason'] ?? '';
|
|
|
|
if (empty($job_id) || empty($reason)) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Missing data']);
|
|
exit;
|
|
}
|
|
|
|
$jobModel = new Job();
|
|
$jobInfo = $jobModel->getByJobNumber($job_id);
|
|
if ($jobInfo) {
|
|
if ($jobModel->cancelJob($jobInfo['id'], $reason, $_SESSION['user_id'])) {
|
|
echo json_encode(['status' => 'success', 'message' => 'ยกเลิกงานสำเร็จ']);
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'ไม่สามารถยกเลิกงานได้']);
|
|
}
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Job not found']);
|
|
}
|
|
} else {
|
|
echo json_encode(['status' => 'error', 'message' => 'Unknown action']);
|
|
}
|