ManagerActionHandler.php

10.72 KB
08/07/2025 09:11
PHP
ManagerActionHandler.php
<?php

namespace App\Handlers;

use App\Core\Database;
use App\Models\Employee;
use App\Models\LeaveBalance;
use App\Models\LeaveRequest;
use App\Models\LeaveType;
use App\Services\TelegramService;

class ManagerActionHandler
{
    private TelegramService $telegramService;
    private int $managerChatId; // Telegram Chat ID of the manager interacting
    private int $managerTelegramUserId; // Telegram User ID of the manager
    private ?int $managerEmployeeId = null; // System's employee ID of the manager

    /**
     * @param TelegramService $telegramService
     * @param int $chatId
     * @param int $telegramUserId
     */
    public function __construct(TelegramService $telegramService, int $chatId, int $telegramUserId)
    {
        $this->telegramService = $telegramService;
        $this->managerChatId = $chatId;
        $this->managerTelegramUserId = $telegramUserId;
        $this->loadManagerEmployeeId();
    }

    private function loadManagerEmployeeId(): void
    {
        $employeeModel = new Employee(Database::getInstance());
        $manager = $employeeModel->findByChatUserId((string) $this->managerTelegramUserId);
        if ($manager && $manager->id) {
            $this->managerEmployeeId = $manager->id;
            // log_message("ManagerActionHandler: Manager Employee ID {$this->managerEmployeeId} loaded for Telegram User ID {$this->managerTelegramUserId}");
        } else {
            log_message("ManagerActionHandler: No employee record found for manager with Telegram User ID {$this->managerTelegramUserId}.");
        }
    }

    /**
     * Handles manager's action (approve/reject) on a leave request.
     * @param string $callbackData e.g., "mgrAction_approve_123" or "mgrAction_reject_123"
     * @param int $originalMessageId The ID of the message manager interacted with (to edit it)
     */
    public function handleApprovalAction(string $callbackData, int $originalMessageId): void
    {
        if ($this->managerEmployeeId === null) {
            $this->telegramService->sendMessage($this->managerChatId, "ขออภัยค่ะ ไม่สามารถดำเนินการได้เนื่องจากไม่พบข้อมูลผู้จัดการ (ID: {$this->managerTelegramUserId}) ของคุณในระบบ");
            return;
        }

        $parts = explode('_', $callbackData);
        if (count($parts) !== 3) {
            $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "ข้อมูลการดำเนินการไม่ถูกต้องค่ะ (invalid callback format)");
            log_message("ManagerActionHandler: Invalid callback data format: {$callbackData}");
            return;
        }

        $action = $parts[1];
        $leaveRequestId = (int) $parts[2];

        $leaveRequestModel = new LeaveRequest(Database::getInstance());
        $request = $leaveRequestModel->read($leaveRequestId);

        if (!$request || !isset($request->id)) {
            $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "ไม่พบคำขอ ID: {$leaveRequestId} ในระบบค่ะ");
            return;
        }

        $requesterEmployeeModel = new Employee(Database::getInstance());
        $requester = $requesterEmployeeModel->read($request->employee_id);
        if (!$requester || !isset($requester->manager_id) || $requester->manager_id !== $this->managerEmployeeId) {
            $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "คุณไม่มีสิทธิ์ดำเนินการกับคำขอ ID: {$leaveRequestId} นี้ค่ะ");
            log_message("Authorization failed: Manager {$this->managerEmployeeId} (Telegram User {$this->managerTelegramUserId}) tried to action request {$leaveRequestId} for employee {$request->employee_id} whose manager is ".($requester->manager_id ?? 'N/A'));
            return;
        }

        if ($request->status !== 'PENDING') {
            $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "คำขอ ID: {$leaveRequestId} ได้รับการดำเนินการไปแล้ว (สถานะปัจจุบัน: {$request->status})");
            return;
        }

        $newStatus = '';
        $actionText = '';
        $notifyRequesterMessage = ''; // Changed from $notifyRequesterText to avoid confusion with boolean result

        // Use the loaded $request object for updates
        $requestForUpdate = new LeaveRequest(Database::getInstance());
        $requestForUpdate->id = $request->id; // Set ID for updateStatus

        if ($action === 'approve') {
            $newStatus = 'APPROVED';
            $actionText = 'อนุมัติ';

            $leaveTypeModel = new LeaveType(Database::getInstance());
            $leaveType = $leaveTypeModel->read($request->leave_type_id);

            $balanceDeductedSuccessfully = true; // Assume success unless deduction fails
            if ($leaveType && $leaveType->deducts_balance) {
                $daysToDeduct = LeaveBalance::calculateLeaveDurationInDays($request->start_date, $request->end_date);
                $currentYear = (int) date('Y', strtotime($request->start_date));

                $leaveBalanceModel = new LeaveBalance(Database::getInstance());
                if ($daysToDeduct > 0) {
                    if (!$leaveBalanceModel->deductFromBalance($request->employee_id, $request->leave_type_id, $currentYear, $daysToDeduct)) {
                        $balanceDeductedSuccessfully = false;
                        log_message("Error deducting balance for approved request {$leaveRequestId}. Still marked APPROVED.");
                    } else {
                        log_message("Successfully deducted {$daysToDeduct} days for request {$leaveRequestId}.");
                    }
                }
            }

            if ($requestForUpdate->updateStatus($newStatus, $this->managerEmployeeId)) {
                $notifyRequesterMessage = "คำขอ{$this->getRequestTypeText($request->leave_type_id)}ของคุณ (ID: {$leaveRequestId}) ได้รับการ *อนุมัติ* แล้วค่ะ";
                if (!$balanceDeductedSuccessfully) {
                    $notifyRequesterMessage .= "\n*(หมายเหตุ: เกิดปัญหาในการอัปเดตยอดวันลาคงเหลือ กรุณาติดต่อ HR)*";
                }
            } else {
                $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "เกิดข้อผิดพลาดในการอนุมัติคำขอ ID: {$leaveRequestId}");
                return;
            }

        } elseif ($action === 'reject') {
            $newStatus = 'REJECTED';
            $actionText = 'ไม่อนุมัติ';
            if ($requestForUpdate->updateStatus($newStatus, $this->managerEmployeeId)) {
                $notifyRequesterMessage = "คำขอ{$this->getRequestTypeText($request->leave_type_id)}ของคุณ (ID: {$leaveRequestId}) ถูก *ปฏิเสธ* ค่ะ";
            } else {
                $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "เกิดข้อผิดพลาดในการไม่อนุมัติคำขอ ID: {$leaveRequestId}");
                return;
            }
        } else {
            $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, "การดำเนินการไม่ถูกต้อง ({$action}) สำหรับคำขอ ID: {$leaveRequestId}");
            return;
        }

        // Sanitize for MarkdownV2
        $escape = function ($text) {return preg_replace('/([_*\[\]()~`>#\+\-=|{}.!\\\\])/', '\\\\$1', $text);};

        $finalMessageToManager = "คุณได้ *{$escape($actionText)}* คำขอ ID: {$escape((string) $leaveRequestId)} เรียบร้อยแล้ว\n";
        $finalMessageToManager .= "(ผู้ขอ: {$escape($requester->first_name)}, ประเภท: {$escape($request->leaveType->name ?? 'N/A')}, วันที่: {$escape($request->start_date)} \\- {$escape($request->end_date)})";

        // Try to edit the message. If it fails (e.g. message too old, or bot removed from chat), send a new one.
        $editSuccess = $this->telegramService->editMessageText($this->managerChatId, $originalMessageId, $finalMessageToManager, null, "MarkdownV2");
        if (!$editSuccess || (isset($editSuccess['ok']) && !$editSuccess['ok'])) {
            log_message("Failed to edit manager's message (MsgID: {$originalMessageId}). Sending new message. Error: ".json_encode($editSuccess));
            $this->telegramService->sendMessage($this->managerChatId, $finalMessageToManager, null, "MarkdownV2");
        }

        if (!empty($notifyRequesterMessage)) {
            $this->notifyRequester($request->employee_id, $leaveRequestId, $newStatus, $notifyRequesterMessage);
        }
    }

    /**
     * @param int $leaveTypeId
     */
    private function getRequestTypeText(int $leaveTypeId): string
    {
        $leaveTypeModel = new LeaveType(Database::getInstance());
        $leaveType = $leaveTypeModel->read($leaveTypeId);
        // Assuming alias 'wfh' is specific to WFH types
        if ($leaveType && isset($leaveType->alias) && strtolower($leaveType->alias) === 'wfh') {
            return " WFH ";
        }
        return "ลา";
    }

    /**
     * @param int $requesterEmployeeId
     * @param int $leaveRequestId
     * @param string $status
     * @param string $messageForRequester
     * @return null
     */
    private function notifyRequester(int $requesterEmployeeId, int $leaveRequestId, string $status, string $messageForRequester): void
    {
        $employeeModel = new Employee(Database::getInstance());
        $requester = $employeeModel->read($requesterEmployeeId);

        if ($requester && isset($requester->chat_user_id) && $requester->chat_user_id) {
            $requesterChatId = (int) $requester->chat_user_id;
            if ($requesterChatId === 0) {
                log_message("Could not notify requester (Employee ID: {$requesterEmployeeId}): Invalid chat_user_id '{$requester->chat_user_id}'");
                return;
            }

            $this->telegramService->sendMessage($requesterChatId, $messageForRequester, null, "MarkdownV2");
            log_message("Notified requester (Employee ID: {$requesterEmployeeId}, Chat ID: {$requesterChatId}) about request {$leaveRequestId} status: {$status}");
        } else {
            log_message("Could not notify requester (Employee ID: {$requesterEmployeeId}): User not found or no chat_user_id.");
        }
    }
}