telegramService = $telegramService; $this->chatId = $chatId; $this->userId = $userId; $this->loadEmployeeId(); } private function loadEmployeeId(): void { $employeeModel = new Employee(Database::getInstance()); $employee = $employeeModel->findByChatUserId((string) $this->userId); if ($employee && $employee->id) { $this->employeeId = $employee->id; // log_message("CancelRequestHandler: Employee ID {$this->employeeId} loaded for Telegram user ID {$this->userId}"); } else { log_message("CancelRequestHandler: No employee record found for Telegram user ID {$this->userId}."); } } // Helper to clear state private function clearUserState(): void { $stateFile = __DIR__."/../../logs/user_{$this->userId}_chat_{$this->chatId}_state.txt"; if (file_exists($stateFile)) { // Only clear states related to cancellation if a specific cancel state exists $currentState = trim(file_get_contents($stateFile)); if (str_starts_with($currentState, 'cancelReq_confirm_')) { if (unlink($stateFile)) { log_message("State cleared by CancelRequestHandler for user {$this->userId} chat {$this->chatId}: {$stateFile}"); } else { log_message("Failed to clear state by CancelRequestHandler for user {$this->userId} chat {$this->chatId}: {$stateFile}"); } } } } // Helper to save state for cancel confirmation /** * @param int $requestId */ private function saveCancellationConfirmState(int $requestId): void { $state = "cancelReq_confirm_{$requestId}"; $stateFilePath = __DIR__."/../../logs/user_{$this->userId}_chat_{$this->chatId}_state.txt"; if (file_put_contents($stateFilePath, $state) === false) { log_message("Failed to save state by CancelRequestHandler to {$stateFilePath}: {$state}"); } else { log_message("State saved by CancelRequestHandler to {$stateFilePath}: {$state}"); } } /** * @return null */ public function startCancellationProcess(): void { if ($this->employeeId === null) { $this->telegramService->sendMessage($this->chatId, "ขออภัยค่ะ ไม่พบข้อมูลพนักงานของคุณในระบบ โปรดติดต่อ HR ค่ะ"); return; } // Clear any previous state of this user for a clean start for this operation. // This is a global clear, might be too broad if user was in another flow. // For a command like /cancelrequest, it's probably fine. $globalStateFile = __DIR__."/../../logs/user_{$this->userId}_chat_{$this->chatId}_state.txt"; if (file_exists($globalStateFile)) { unlink($globalStateFile); log_message("Global state cleared by startCancellationProcess for user {$this->userId} chat {$this->chatId}"); } $leaveRequestModel = new LeaveRequest(Database::getInstance()); $pendingRequests = $leaveRequestModel->getPendingRequestsByEmployee($this->employeeId); if (empty($pendingRequests)) { $this->telegramService->sendMessage($this->chatId, "คุณไม่ม่ีคำขอลา/WFH ที่อยู่ในสถานะ 'รออนุมัติ' ที่สามารถยกเลิกได้ค่ะ"); return; } $messageText = "รายการคำขอที่รออนุมัติของคุณ (เลือกเพื่อยกเลิก):\n"; $keyboardButtons = []; foreach ($pendingRequests as $request) { // $request is a LeaveRequest object, $request->leaveType should be LeaveType object $leaveTypeName = ($request->leaveType && $request->leaveType->name) ? htmlspecialchars($request->leaveType->name) : 'N/A'; $startDate = ($request->start_date) ? (new DateTime($request->start_date))->format('d/m/Y') : 'N/A'; $endDate = ($request->end_date) ? (new DateTime($request->end_date))->format('d/m/Y') : 'N/A'; $buttonText = "ID: {$request->id} - {$leaveTypeName} ({$startDate} - {$endDate})"; $keyboardButtons[] = [['text' => $buttonText, 'callback_data' => "cancelReq_select_{$request->id}"]]; } if (empty($keyboardButtons)) { $this->telegramService->sendMessage($this->chatId, "ไม่สามารถสร้างรายการคำขอของคุณได้ค่ะ"); return; } $replyMarkup = ['inline_keyboard' => $keyboardButtons]; $this->telegramService->sendMessage($this->chatId, $messageText, $replyMarkup); } /** * @param string $callbackData * @return null */ public function handleCancellationSelection(string $callbackData): void { // cancelReq_select_{requestId} if ($this->employeeId === null) {$this->telegramService->sendMessage($this->chatId, "ขออภัยค่ะ ไม่พบข้อมูลพนักงาน");return;} $parts = explode('_', $callbackData); $requestId = (int) end($parts); $leaveRequestModel = new LeaveRequest(Database::getInstance()); $requestToCancel = $leaveRequestModel->read($requestId); if (!$requestToCancel || $requestToCancel->employee_id !== $this->employeeId) { $this->telegramService->sendMessage($this->chatId, "ไม่พบคำขอที่คุณเลือก หรือคุณไม่มีสิทธิ์ยกเลิกคำขอนี้ค่ะ"); return; } if ($requestToCancel->status !== 'PENDING') { $this->telegramService->sendMessage($this->chatId, "คำขอ ID: {$requestId} ไม่อยู่ในสถานะ 'รออนุมัติ' จึงไม่สามารถยกเลิกผ่านระบบนี้ได้ค่ะ (สถานะปัจจุบัน: {$requestToCancel->status})"); return; } $leaveTypeName = ($requestToCancel->leaveType && $requestToCancel->leaveType->name) ? $requestToCancel->leaveType->name : 'N/A'; $startDate = ($requestToCancel->start_date) ? (new DateTime($requestToCancel->start_date))->format('d/m/Y') : 'N/A'; $endDate = ($requestToCancel->end_date) ? (new DateTime($requestToCancel->end_date))->format('d/m/Y') : 'N/A'; $reason = $requestToCancel->reason; $escapeMarkdown = function ($text) { if ($text === null) { return ""; } return preg_replace('/([_*\[\]()~`>#\+\-=|{}.!\\\\])/', '\\\\$1', $text); }; $confirmMessage = "คุณต้องการยกเลิกคำขอลา/WFH ต่อไปนี้ใช่หรือไม่?\n\n"; $confirmMessage .= "*ID:* {$escapeMarkdown((string) $requestToCancel->id)}\n"; $confirmMessage .= "*ประเภท:* {$escapeMarkdown($leaveTypeName)}\n"; $confirmMessage .= "*วันที่:* {$escapeMarkdown($startDate)} \\- {$escapeMarkdown($endDate)}\n"; if ($reason) { $confirmMessage .= "*เหตุผล:* {$escapeMarkdown($reason)}\n"; } $this->saveCancellationConfirmState($requestId); $keyboardButtons = [ [['text' => '✅ ใช่, ยืนยันการยกเลิก', 'callback_data' => "cancelReq_confirm_{$requestId}"]], [['text' => '❌ ไม่, กลับไปก่อน', 'callback_data' => 'cancelReq_abort']] ]; $replyMarkup = ['inline_keyboard' => $keyboardButtons]; $this->telegramService->sendMessage($this->chatId, $confirmMessage, $replyMarkup, 'MarkdownV2'); } /** * @param string $stateData * @param string $actionCallbackData * @return null */ public function handleCancellationConfirmation(string $stateData, string $actionCallbackData): void { if ($this->employeeId === null) { $this->telegramService->sendMessage($this->chatId, "ขออภัยค่ะ ไม่พบข้อมูลพนักงาน"); $this->clearUserState();return; } if ($actionCallbackData === 'cancelReq_abort') { $this->telegramService->sendMessage($this->chatId, "การยกเลิกถูกระงับค่ะ คุณสามารถเลือกคำขออื่นเพื่อยกเลิก หรือใช้คำสั่งอื่นได้ค่ะ"); $this->clearUserState(); // $this->startCancellationProcess(); // Optionally re-show list return; } $actionParts = explode('_', $actionCallbackData); // e.g. cancelReq_confirm_123 $requestIdFromAction = (int) end($actionParts); $stateParts = explode('_', $stateData); // e.g. cancelReq_confirm_123 $requestIdFromState = (int) end($stateParts); if (!str_starts_with($actionCallbackData, "cancelReq_confirm_") || !str_starts_with($stateData, "cancelReq_confirm_") || $requestIdFromAction !== $requestIdFromState) { $this->telegramService->sendMessage($this->chatId, "ข้อมูลการยืนยันไม่ตรงกันค่ะ กรุณาลองใหม่"); log_message("Mismatched confirmation data. Action: {$actionCallbackData}, State: {$stateData}"); $this->clearUserState(); return; } $requestId = $requestIdFromAction; $leaveRequestModel = new LeaveRequest(Database::getInstance()); // It's crucial that read() re-fetches and populates the object fully, including its current status. $requestToCancel = $leaveRequestModel->read($requestId); if (!$requestToCancel || !isset($requestToCancel->employee_id) || $requestToCancel->employee_id !== $this->employeeId) { $this->telegramService->sendMessage($this->chatId, "ไม่พบคำขอ (ID: {$requestId}) หรือคุณไม่มีสิทธิ์ยกเลิกคำขอนี้ค่ะ"); $this->clearUserState(); return; } if ($requestToCancel->status !== 'PENDING') { $this->telegramService->sendMessage($this->chatId, "คำขอ ID: {$requestId} ไม่อยู่ในสถานะ 'รออนุมัติ' แล้วค่ะ (สถานะปัจจุบัน: {$requestToCancel->status})"); $this->clearUserState(); return; } // Use the existing updateStatus method of the loaded $requestToCancel object // This requires $requestToCancel to be the object instance from read($requestId) $requestToCancel->db = Database::getInstance(); // Ensure db is set if read() doesn't return $this // Or, better, ensure read() returns the main object instance // For now, let's re-instantiate for the update to be safe if read() returns a generic obj/array $updateModel = new LeaveRequest(Database::getInstance()); $updateModel->id = $requestId; // Set the ID of the request to update if ($updateModel->updateStatus('CANCELLED', $this->employeeId)) { $this->telegramService->sendMessage($this->chatId, "✅ คำขอลา/WFH ID: {$requestId} ของคุณถูกยกเลิกเรียบร้อยแล้วค่ะ"); } else { $this->telegramService->sendMessage($this->chatId, "❌ เกิดข้อผิดพลาดในการยกเลิกคำขอ ID: {$requestId} กรุณาลองใหม่อีกครั้ง หรือติดต่อผู้ดูแลค่ะ"); error_log("Failed to cancel leave request ID {$requestId} for employee {$this->employeeId}."); } $this->clearUserState(); } }