1602 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			1602 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			PHP
		
	
	
	
<?php
 | 
						|
 | 
						|
namespace Modules\Admin\app\Http\Controllers;
 | 
						|
 | 
						|
use App\Http\Controllers\Controller;
 | 
						|
use App\Mail\TicketMail;
 | 
						|
use App\Models\Notes;
 | 
						|
use App\Traits\AnalyzeData;
 | 
						|
use App\Traits\HasFilterRequest;
 | 
						|
use App\Traits\HasOrderByRequest;
 | 
						|
use App\Traits\HasSearchRequest;
 | 
						|
use Carbon\Carbon;
 | 
						|
use Carbon\CarbonPeriod;
 | 
						|
use Illuminate\Http\Request;
 | 
						|
use Illuminate\Support\Facades\DB;
 | 
						|
use Illuminate\Support\Facades\Mail;
 | 
						|
use Modules\Admin\app\Models\Admin;
 | 
						|
use Modules\Admin\app\Models\Category;
 | 
						|
use Modules\Admin\app\Models\Ticket;
 | 
						|
use Modules\Admin\app\Models\Tracking;
 | 
						|
use Illuminate\Support\Facades\Log;
 | 
						|
use App\Models\LeaveDays;
 | 
						|
use App\Models\Admin as UserModel;
 | 
						|
 | 
						|
class TicketController extends Controller
 | 
						|
{
 | 
						|
    use HasOrderByRequest;
 | 
						|
    use HasFilterRequest;
 | 
						|
    use HasSearchRequest;
 | 
						|
    use AnalyzeData;
 | 
						|
 | 
						|
    public function getByUserId(Request $request)
 | 
						|
    {
 | 
						|
        $tickets = new Ticket;
 | 
						|
 | 
						|
        // Order by
 | 
						|
        $this->orderByRequest($tickets, $request);
 | 
						|
 | 
						|
        // Filter
 | 
						|
        $this->filterRequest(
 | 
						|
            builder: $tickets,
 | 
						|
            request: $request,
 | 
						|
            filterKeys: [
 | 
						|
                'type' => self::F_TEXT,
 | 
						|
                'reason' => self::F_TEXT,
 | 
						|
                'updated_by' => self::F_TEXT,
 | 
						|
                'start_date' => [
 | 
						|
                    'type' => self::F_THAN_EQ_DATETIME,
 | 
						|
                    'column' => 'tickets.start_date'
 | 
						|
                ],
 | 
						|
                'end_date' => [
 | 
						|
                    'type' => self::F_THAN_EQ_DATETIME,
 | 
						|
                    'column' => 'tickets.end_date'
 | 
						|
                ],
 | 
						|
            ]
 | 
						|
        );
 | 
						|
 | 
						|
        $this->searchRequest(
 | 
						|
            builder: $tickets,
 | 
						|
            value: $request->get('search'),
 | 
						|
            fields: [
 | 
						|
                'users.name',
 | 
						|
                "start_date",
 | 
						|
                "startPeriod.c_name",
 | 
						|
                "end_date",
 | 
						|
                "endPeriod.c_name",
 | 
						|
                "typeReason.c_name",
 | 
						|
                'status',
 | 
						|
                "reason",
 | 
						|
            ]
 | 
						|
        );
 | 
						|
 | 
						|
        $responseData = array_merge(
 | 
						|
            $tickets
 | 
						|
                ->join('users', 'tickets.user_id', '=', 'users.id')
 | 
						|
                ->leftJoin("categories as startPeriod", function ($join) {
 | 
						|
                    $join->on('start_period', '=', 'startPeriod.c_code');
 | 
						|
                    $join->on('startPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
 | 
						|
                })
 | 
						|
                ->leftJoin("categories as endPeriod", function ($join) {
 | 
						|
                    $join->on('end_period', '=', 'endPeriod.c_code');
 | 
						|
                    $join->on('endPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
 | 
						|
                })
 | 
						|
                ->leftJoin("categories as typeReason", function ($join) {
 | 
						|
                    $join->on('type', '=', 'typeReason.c_code');
 | 
						|
                    $join->on('typeReason.c_type', DB::raw("CONCAT('REASON')"));
 | 
						|
                })
 | 
						|
                ->leftJoin("categories as statusTickets", function ($join) {
 | 
						|
                    $join->on('status', '=', 'statusTickets.c_code');
 | 
						|
                    $join->on('statusTickets.c_type', DB::raw("CONCAT('STATUS_TICKETS')"));
 | 
						|
                })
 | 
						|
                ->where('user_id', auth('admins')->user()->id)->orderBy('tickets.created_at', 'desc')
 | 
						|
                ->select(
 | 
						|
                    'users.name as user_name',
 | 
						|
                    'users.email',
 | 
						|
                    'tickets.*',
 | 
						|
                    'startPeriod.c_name as startPeriodName',
 | 
						|
                    'endPeriod.c_name as endPeriodName',
 | 
						|
                    'typeReason.c_name as typeReasonName',
 | 
						|
                    'statusTickets.c_name as statusTicketsName',
 | 
						|
                )
 | 
						|
                ->paginate($request->get('per_page'))->toArray(),
 | 
						|
            ['status' => true]
 | 
						|
        );
 | 
						|
        return response()->json($responseData);
 | 
						|
    }
 | 
						|
 | 
						|
    public function getAll(Request $request)
 | 
						|
    {
 | 
						|
        $tickets = new Ticket;
 | 
						|
 | 
						|
        // Order by
 | 
						|
        $this->orderByRequest($tickets, $request);
 | 
						|
 | 
						|
        // Filter
 | 
						|
        $this->filterRequest(
 | 
						|
            builder: $tickets,
 | 
						|
            request: $request,
 | 
						|
            filterKeys: [
 | 
						|
                'type' => self::F_TEXT,
 | 
						|
                'reason' => self::F_TEXT,
 | 
						|
                'updated_by' => self::F_TEXT,
 | 
						|
                'start_date' => [
 | 
						|
                    'type' => self::F_THAN_EQ_DATETIME,
 | 
						|
                    'column' => 'tickets.start_date'
 | 
						|
                ],
 | 
						|
                'end_date' => [
 | 
						|
                    'type' => self::F_THAN_EQ_DATETIME,
 | 
						|
                    'column' => 'tickets.end_date'
 | 
						|
                ],
 | 
						|
            ]
 | 
						|
        );
 | 
						|
 | 
						|
        $this->searchRequest(
 | 
						|
            builder: $tickets,
 | 
						|
            value: $request->get('search'),
 | 
						|
            fields: [
 | 
						|
                'users.name',
 | 
						|
                "start_date",
 | 
						|
                "startPeriod.c_name",
 | 
						|
                "end_date",
 | 
						|
                "endPeriod.c_name",
 | 
						|
                "typeReason.c_name",
 | 
						|
                'status',
 | 
						|
                "reason",
 | 
						|
                "admin_note"
 | 
						|
            ]
 | 
						|
        );
 | 
						|
 | 
						|
        if ($request->typeReason != '') {
 | 
						|
            $tickets = $tickets->where('tickets.type', '=', $request->typeReason);
 | 
						|
        }
 | 
						|
        if ($request->statusFilter != '') {
 | 
						|
            $tickets = $tickets->where('tickets.status', '=', $request->statusFilter);
 | 
						|
        }
 | 
						|
 | 
						|
        $responseData = array_merge(
 | 
						|
            $tickets
 | 
						|
                ->join('users', 'tickets.user_id', '=', 'users.id')
 | 
						|
                ->leftJoin("categories as startPeriod", function ($join) {
 | 
						|
                    $join->on('start_period', '=', 'startPeriod.c_code');
 | 
						|
                    $join->on('startPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
 | 
						|
                })
 | 
						|
                ->leftJoin("categories as endPeriod", function ($join) {
 | 
						|
                    $join->on('end_period', '=', 'endPeriod.c_code');
 | 
						|
                    $join->on('endPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
 | 
						|
                })
 | 
						|
                ->leftJoin("categories as typeReason", function ($join) {
 | 
						|
                    $join->on('type', '=', 'typeReason.c_code');
 | 
						|
                    $join->on('typeReason.c_type', DB::raw("CONCAT('REASON')"));
 | 
						|
                })
 | 
						|
                ->leftJoin("categories as statusTickets", function ($join) {
 | 
						|
                    $join->on('status', '=', 'statusTickets.c_code');
 | 
						|
                    $join->on('statusTickets.c_type', DB::raw("CONCAT('STATUS_TICKETS')"));
 | 
						|
                })
 | 
						|
                ->select(
 | 
						|
                    'users.name as user_name',
 | 
						|
                    'users.email',
 | 
						|
                    'tickets.*',
 | 
						|
                    'startPeriod.c_name as startPeriodName',
 | 
						|
                    'endPeriod.c_name as endPeriodName',
 | 
						|
                    'typeReason.c_name as typeReasonName',
 | 
						|
                    'statusTickets.c_name as statusTicketsName',
 | 
						|
                )
 | 
						|
                ->orderBy('tickets.created_at', 'desc')->paginate($request->get('per_page'))->toArray(),
 | 
						|
            ['status' => true]
 | 
						|
        );
 | 
						|
 | 
						|
        return response()->json($responseData);
 | 
						|
    }
 | 
						|
 | 
						|
    public function createTicket(Request $request)
 | 
						|
    {
 | 
						|
        // Validate input
 | 
						|
        $rules = [
 | 
						|
            'start_date' => 'required|date',
 | 
						|
            'start_period' => 'required|string',
 | 
						|
            'end_date' => 'required|date|after_or_equal:start_date',
 | 
						|
            'end_period' => 'required|string',
 | 
						|
            'type' => 'required|string',
 | 
						|
        ];
 | 
						|
        $request->validate($rules);
 | 
						|
 | 
						|
        // Get input data
 | 
						|
        $startDate = $request->input('start_date');
 | 
						|
        $startPeriod = $request->input('start_period');
 | 
						|
        $endDate = $request->input('end_date');
 | 
						|
        $endPeriod = $request->input('end_period');
 | 
						|
        $type = $request->input('type');
 | 
						|
        $reason = $request->input('reason');
 | 
						|
        $isAccept = $request->input('is_accept') ?? false;
 | 
						|
        $user = auth('admins')->user();
 | 
						|
 | 
						|
        $start_date = Carbon::create($startDate)->setTimezone(env('TIME_ZONE'));
 | 
						|
        $end_date = Carbon::create($endDate)->setTimezone(env('TIME_ZONE'));
 | 
						|
 | 
						|
        $dataListPeriod = $this->getAllPeriodNew($start_date, $startPeriod, $end_date, $endPeriod);
 | 
						|
        if (empty($dataListPeriod)) {
 | 
						|
            return AbstractController::ResultError('Không thể tính toán khoảng thời gian nghỉ hợp lệ.');
 | 
						|
        }
 | 
						|
 | 
						|
        // Lấy các ticket của user trong thời gian request
 | 
						|
        $userTickets = Ticket::where('user_id', $user->id)
 | 
						|
            ->whereIn('status', ['WAITING', 'CONFIRMED'])
 | 
						|
            ->where(function ($query) use ($start_date, $end_date) {
 | 
						|
                $query->where(function ($q) use ($start_date, $end_date) {
 | 
						|
                    // Trường hợp 1: start_date nằm trong khoảng
 | 
						|
                    $q->whereBetween(DB::raw('DATE(start_date)'), [$start_date->toDateString(), $end_date->toDateString()]);
 | 
						|
                })
 | 
						|
                    ->orWhere(function ($q) use ($start_date, $end_date) {
 | 
						|
                        // Trường hợp 2: end_date nằm trong khoảng
 | 
						|
                        $q->whereBetween(DB::raw('DATE(end_date)'), [$start_date->toDateString(), $end_date->toDateString()]);
 | 
						|
                    })
 | 
						|
                    ->orWhere(function ($q) use ($start_date, $end_date) {
 | 
						|
                        // Trường hợp 3: Khoảng thời gian được chọn nằm trong khoảng của ticket
 | 
						|
                        $q->where(DB::raw('DATE(start_date)'), '<=', $start_date->toDateString())
 | 
						|
                            ->where(DB::raw('DATE(end_date)'), '>=', $end_date->toDateString());
 | 
						|
                    });
 | 
						|
            })
 | 
						|
            ->get();
 | 
						|
        $userTicketListPeriod = [];
 | 
						|
        if ($userTickets->count() > 0) {
 | 
						|
            foreach ($userTickets as $ticket) {
 | 
						|
                $userTicketListPeriod = array_merge($userTicketListPeriod, $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Kiểm tra ticket tạo có trùng thời gian với các ticket cũ 
 | 
						|
        $periodStrings = [];
 | 
						|
        $userTicketPeriodStrings = [];
 | 
						|
        foreach ($dataListPeriod as $period) {
 | 
						|
            if ($period['period'] == 'ALL') {
 | 
						|
                $periodStrings[] = $period['date'] . '_S';
 | 
						|
                $periodStrings[] = $period['date'] . '_C';
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            $periodStrings[] = $period['date'] . '_' . $period['period'];
 | 
						|
        }
 | 
						|
        foreach ($userTicketListPeriod as $period) {
 | 
						|
            if ($period['period'] == 'ALL') {
 | 
						|
                $userTicketPeriodStrings[] = $period['date'] . '_S';
 | 
						|
                $userTicketPeriodStrings[] = $period['date'] . '_C';
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            $userTicketPeriodStrings[] = $period['date'] . '_' . $period['period'];
 | 
						|
        }
 | 
						|
 | 
						|
        if (count(array_intersect($periodStrings, $userTicketPeriodStrings)) > 0) {
 | 
						|
            return AbstractController::ResultError('Đã có ticket được tạo trong thời gian này, không thể tạo ticket mới!');
 | 
						|
        }
 | 
						|
 | 
						|
        // Kiểm tra khi type = ONLEAVE (nghỉ phép)
 | 
						|
        if ($type === 'ONLEAVE' && !$isAccept) {
 | 
						|
            // Lấy tickets nghỉ phép đang ở trạng thái WAITING
 | 
						|
            $ticketsWaiting = Ticket::where('user_id', $user->id)->where('status', 'WAITING')->where('type', 'ONLEAVE')
 | 
						|
                ->get();
 | 
						|
            $dataListPeriodWaiting = [];
 | 
						|
            if ($ticketsWaiting->count() > 0) {
 | 
						|
                foreach ($ticketsWaiting as $ticket) {
 | 
						|
                    $dataListPeriodWaiting = array_merge($dataListPeriodWaiting, $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period));
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Tạo thông báo về tickets waiting nếu có
 | 
						|
            $waitingTicketsMessage = '';
 | 
						|
            if (!empty($dataListPeriodWaiting)) {
 | 
						|
                // Kiểm tra số dư ngày phép cho tickets waiting
 | 
						|
                $waitingTicketsMessage = "Bạn đang có " . $ticketsWaiting->count() . " yêu cầu nghỉ phép chưa được duyệt";
 | 
						|
 | 
						|
                // Nếu muốn thêm chi tiết từng ticket waiting
 | 
						|
                if ($ticketsWaiting->count() > 0) {
 | 
						|
                    $waitingTicketsMessage .= ":\n";
 | 
						|
                    foreach ($ticketsWaiting as $ticket) {
 | 
						|
                        $startDateFormat = Carbon::parse($ticket->start_date)->format('d/m/Y');
 | 
						|
                        $endDateFormat = Carbon::parse($ticket->end_date)->format('d/m/Y');
 | 
						|
                        $waitingTicketsMessage .= "- " . $ticket->startPeriodName . " (" . $startDateFormat . ") - " .
 | 
						|
                            $ticket->endPeriodName . " (" . $endDateFormat . ")\n";
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            $balanceCheckResultWaiting = $this->checkLeaveBalance($user, $dataListPeriodWaiting);
 | 
						|
            // dd($balanceCheckResultWaiting,$dataListPeriodWaiting,$user);
 | 
						|
            if ($balanceCheckResultWaiting['months_info']) {
 | 
						|
                $monthsInfoWaiting = $balanceCheckResultWaiting['months_info'];
 | 
						|
                if ($balanceCheckResultWaiting['success']) {
 | 
						|
                    $waitingTicketsMessage .= "------------------------------------------------";
 | 
						|
                } else {
 | 
						|
                    $waitingTicketsMessage .=  $balanceCheckResultWaiting['message'] . "\n------------------------------------------------";
 | 
						|
                }
 | 
						|
                $balanceCheckResult = $this->checkLeaveBalance($user, $dataListPeriod, $monthsInfoWaiting);
 | 
						|
                // dd($balanceCheckResult, $waitingTicketsMessage);
 | 
						|
            } else {
 | 
						|
                $balanceCheckResult = $this->checkLeaveBalance($user, $dataListPeriod);
 | 
						|
            }
 | 
						|
            // dd($balanceCheckResult);
 | 
						|
            // Nếu không đủ ngày phép, trả về thông báo và chưa tạo ticket
 | 
						|
            if (!$balanceCheckResult['success']) {
 | 
						|
                $finalMessage = $waitingTicketsMessage;
 | 
						|
                if (!empty($finalMessage)) {
 | 
						|
                    $finalMessage .= "\n\n";
 | 
						|
                }
 | 
						|
                $finalMessage .= $balanceCheckResult['message'];
 | 
						|
                $balanceCheckResult['message'] = $finalMessage . "\n\nBạn có chấp nhận không?\n";
 | 
						|
                $balanceCheckResult['waitingTicketMessage'] = $waitingTicketsMessage;
 | 
						|
 | 
						|
                return AbstractController::ResultError("Không thỏa mãn điều kiện ngày phép", $balanceCheckResult);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // --- Kết thúc kiểm tra ---
 | 
						|
 | 
						|
        // Tạo ticket mới khi:
 | 
						|
        // - Ticket được tạo không trùng ngày
 | 
						|
        // - User có đủ phép
 | 
						|
        // - User không đủ phép nhưng vẫn đồng ý tạo
 | 
						|
        $ticket = Ticket::create([
 | 
						|
            'start_date' => $start_date->toDateString(),
 | 
						|
            'start_period' => $startPeriod,
 | 
						|
            'end_date' => $end_date->toDateString(),
 | 
						|
            'end_period' => $endPeriod,
 | 
						|
            'type' => $type,
 | 
						|
            'status' => 'WAITING',
 | 
						|
            'reason' => $reason,
 | 
						|
            'user_id' => $user->id
 | 
						|
        ]);
 | 
						|
 | 
						|
        // Send notification email to admin (list)
 | 
						|
        $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $startPeriod);
 | 
						|
        $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $endPeriod);
 | 
						|
        $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $type);
 | 
						|
 | 
						|
        $formattedStartDate = Carbon::createFromFormat('Y-m-d', $startDate)->format('d/m/Y');
 | 
						|
        $formattedEndDate = Carbon::createFromFormat('Y-m-d', $endDate)->format('d/m/Y');
 | 
						|
 | 
						|
        $admins = Admin::where('permission', 'like', '%admin%')->get();
 | 
						|
        foreach ($admins as $key => $value) {
 | 
						|
            $data = array(
 | 
						|
                "ticket_id" => $ticket->id,
 | 
						|
                "email_template" => "email.notification_tickets",
 | 
						|
                "email" => $user->email,
 | 
						|
                "admin_email" => $value->email,
 | 
						|
                "name" => $user->name,
 | 
						|
                "date" => optional($dataMasterStartPeriod)->c_name . " (" . $formattedStartDate . ") - " . optional($dataMasterEndPeriod)->c_name . " (" . $formattedEndDate . ")",
 | 
						|
                "type" => optional($dataMasterType)->c_name,
 | 
						|
                "note" => $reason,
 | 
						|
                "link" => "/tickets-management", //link đến page admin
 | 
						|
                "subject" => "[Ticket request] Ticket From " . $user->name
 | 
						|
            );
 | 
						|
            // Thêm kiểm tra null trước khi gửi mail
 | 
						|
            if ($dataMasterStartPeriod && $dataMasterEndPeriod && $dataMasterType) {
 | 
						|
                Mail::to($value->email)->queue(new TicketMail($data));
 | 
						|
            } else {
 | 
						|
                Log::error("Missing category data for ticket ID: {$ticket->id}. Mail not sent.");
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return response()->json(['data' => $ticket, 'status' => true]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function updateTicket(Request $request)
 | 
						|
    {
 | 
						|
        // Validate input
 | 
						|
        $rules = [
 | 
						|
            'ticket_id' => 'required|exists:tickets,id',
 | 
						|
            'status' => 'required|string|in:CONFIRMED,REFUSED'
 | 
						|
        ];
 | 
						|
        $request->validate($rules);
 | 
						|
 | 
						|
        $ticket = Ticket::find($request->input('ticket_id'));
 | 
						|
        if (!$ticket) {
 | 
						|
            return AbstractController::ResultError("Ticket not found.");
 | 
						|
        }
 | 
						|
 | 
						|
        $admin = auth('admins')->user();
 | 
						|
        // Delete related note, if status change to Refuse
 | 
						|
        if ($request->status == "REFUSED") {
 | 
						|
            $ticket->status = "REFUSED";
 | 
						|
 | 
						|
            // Handle send mail
 | 
						|
            $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->start_period);
 | 
						|
            $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $ticket->end_period);
 | 
						|
            $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $ticket->type);
 | 
						|
            $formattedStartDate = Carbon::createFromFormat('Y-m-d', $ticket->start_date)->format('d/m/Y');
 | 
						|
            $formattedEndDate = Carbon::createFromFormat('Y-m-d', $ticket->end_date)->format('d/m/Y');
 | 
						|
 | 
						|
            $user = Admin::find($ticket->user_id);
 | 
						|
 | 
						|
            $data = array(
 | 
						|
                "email_template" => "email.notification_tickets_user",
 | 
						|
                "user_name" => $user->name,
 | 
						|
                "email" => $user->email,
 | 
						|
                "name" => $admin->name, //name admin duyệt
 | 
						|
                "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
						|
                "type" => $dataMasterType->c_name,
 | 
						|
                "note" => $ticket->reason,
 | 
						|
                "admin_note" => $ticket->admin_note,
 | 
						|
                "link" => "/tickets", //link đến page admin
 | 
						|
                "status" => "refused",
 | 
						|
                "subject" => "[Ticket response] Ticket From " . $admin->name
 | 
						|
            );
 | 
						|
            Mail::to($user->email)->send(new TicketMail($data));
 | 
						|
 | 
						|
            Notes::where('ticket_id', $ticket->id)->delete();
 | 
						|
        }
 | 
						|
 | 
						|
        $ticket->updated_by = $admin->name;
 | 
						|
        $ticket->admin_note = $request->admin_note;
 | 
						|
 | 
						|
        // Clear Timekeeping cache
 | 
						|
        $ticket->save();
 | 
						|
 | 
						|
        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
 | 
						|
        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
 | 
						|
 | 
						|
        return AbstractController::ResultSuccess($ticket, "Ticket updated successfully!");
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Kiểm tra số dư ngày phép của người dùng.
 | 
						|
     *
 | 
						|
     * @param UserModel $user Người dùng tạo ticket
 | 
						|
     * @param array|null $dataListPeriod Danh sách các ngày xin nghỉ [['date' => 'Y-m-d', 'period' => 'ALL|S|C'], ...]
 | 
						|
     * @return array Kết quả kiểm tra ['success' => bool, 'message' => string|null, ...]
 | 
						|
     */
 | 
						|
    private function checkLeaveBalance($user, ?array $dataListPeriod = null, ?array $monthsInfoWaiting = null, ?bool $isAccept = false): array
 | 
						|
    {
 | 
						|
        // Kiểm tra giới hạn nghỉ phép theo tháng
 | 
						|
        if (!empty($dataListPeriod)) {
 | 
						|
            return $this->checkMonthlyLeaveLimit($user, $dataListPeriod, $monthsInfoWaiting, $isAccept);
 | 
						|
        }
 | 
						|
 | 
						|
        // Đủ điều kiện
 | 
						|
        return [
 | 
						|
            'success' => true,
 | 
						|
            'message' => null,
 | 
						|
            'months_info' => []
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    private function getTotalAllocatedDays($user, int $year): float
 | 
						|
    {
 | 
						|
        $leaveDaysInfo = LeaveDays::where('ld_user_id', $user->id)
 | 
						|
            ->where('ld_year', $year)
 | 
						|
            ->first();
 | 
						|
 | 
						|
        $totalAllocated = 0;
 | 
						|
        if ($leaveDaysInfo) {
 | 
						|
            $totalAllocated = $leaveDaysInfo->ld_day_total + $leaveDaysInfo->ld_additional_day + $leaveDaysInfo->ld_special_leave_day;
 | 
						|
        } else {
 | 
						|
            Log::warning("No LeaveDays record found for user ID: {$user->id}, year: {$year}. Assuming 0 allocated days.");
 | 
						|
        }
 | 
						|
 | 
						|
        return $totalAllocated;
 | 
						|
    }
 | 
						|
 | 
						|
    private function getUsedLeaveDays($user, int $year): float
 | 
						|
    {
 | 
						|
        return Notes::join('categories', function ($join) {
 | 
						|
            $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
						|
                ->where('categories.c_type', 'TIME_TYPE');
 | 
						|
        })
 | 
						|
            ->where('n_user_id', $user->id)
 | 
						|
            ->where('n_year', $year)
 | 
						|
            ->where('n_reason', 'ONLEAVE')
 | 
						|
            ->sum('categories.c_value');
 | 
						|
    }
 | 
						|
 | 
						|
    //Tính tổng giới hạn ngày nghỉ có phép tối đa trong tháng
 | 
						|
    private function getMaxLeaveDaysPerMonth(): int
 | 
						|
    {
 | 
						|
        $limitLeaveMonth = Category::where('c_type', 'LIMIT_LEAVE_MONTH')->where('c_code', "LIMIT")->first();
 | 
						|
        if ($limitLeaveMonth) {
 | 
						|
            $maxDaysPerMonth = (int)$limitLeaveMonth->c_value;
 | 
						|
        } else {
 | 
						|
            $maxDaysPerMonth = 3; // default nếu k có setting
 | 
						|
        }
 | 
						|
        return $maxDaysPerMonth;
 | 
						|
    }
 | 
						|
 | 
						|
    private function checkMonthlyLeaveLimit($user, array $dataListPeriod, ?array $monthsInfoWaiting = null, ?bool $isAccept = false): array
 | 
						|
    {
 | 
						|
        // Danh sách ngày nghỉ theo tháng
 | 
						|
        $requestMonths = $this->groupLeaveRequestsByMonth($dataListPeriod);
 | 
						|
        $monthsInfo = [];
 | 
						|
        $hasInsufficientDays = false;
 | 
						|
        $errorMessage = '';
 | 
						|
        $remainingDaysInMonthIsUsed = 0;
 | 
						|
 | 
						|
        // Tổng giới hạn ngày nghỉ có phép tối đa trong tháng
 | 
						|
        $maxDaysPerMonth = $this->getMaxLeaveDaysPerMonth();
 | 
						|
        $monthIndex = 0;
 | 
						|
        $onleaveTmp = 0; // Ngày phép trừ tạm (tính phép cho nhiều tháng)
 | 
						|
 | 
						|
        foreach ($requestMonths as $monthData) {
 | 
						|
            if ($monthsInfoWaiting) {
 | 
						|
                foreach ($monthsInfoWaiting as $monthInfo) {
 | 
						|
                    if ($monthInfo['month'] == $monthData['month'] && $monthInfo['year'] == $monthData['year']) {
 | 
						|
                        $remainingDaysInMonthIsUsed += $monthInfo['remaining_days_in_month_remaining'];
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Số ngày nghỉ trong tháng
 | 
						|
            $onleaveDaysInMonth = $this->getUsedLeaveDaysInMonth($user, $monthData['year'], $monthData['month'], 'ONLEAVE'); // Có phép
 | 
						|
            $nopayDaysInMonth = $this->getUsedLeaveDaysInMonth($user, $monthData['year'], $monthData['month'], 'LEAVE_WITHOUT_PAY'); // Không phép
 | 
						|
            $usedDaysInMonth = $onleaveDaysInMonth + $nopayDaysInMonth; // Tổng
 | 
						|
 | 
						|
            // Tổng ngày nghỉ sẽ dùng trong tháng
 | 
						|
            $willUsedDaysInMonth = $usedDaysInMonth + $monthData['days_requested'];
 | 
						|
 | 
						|
            // Ngày phép
 | 
						|
            $onleaveDaysTotal = $this->getTotalLeaveDaysInMonth($user, $monthData['year'], $monthData['month'], $isAccept); // Tổng phép của user
 | 
						|
            $usedOnleaveDaysTotal = $this->getTotalLeaveDaysInMonthToMonth($user, $monthData['year'], $monthData['month']); // Phép đã dùng
 | 
						|
            $remainingOnleaveDays = $onleaveDaysTotal - $usedOnleaveDaysTotal - $onleaveTmp; // Phép còn lại
 | 
						|
 | 
						|
            // Tổng ngày phép còn lại trong tháng
 | 
						|
            $remainingOnleaveDaysInMonth = $remainingOnleaveDays - $remainingDaysInMonthIsUsed;
 | 
						|
 | 
						|
            // Log::debug(
 | 
						|
            //     "📊 Thống kê ngày phép:\n" .
 | 
						|
            //         " - Tháng: {$monthData['month']}\n" .
 | 
						|
            //         " - Tổng ngày nghỉ có phép trong tháng: $onleaveDaysInMonth\n" .
 | 
						|
            //         " - Tổng ngày nghỉ không phép trong tháng: $nopayDaysInMonth\n" .
 | 
						|
            //         " - Tổng ngày nghỉ đã dùng trong tháng: $usedDaysInMonth\n" .
 | 
						|
            //         " - Tổng ngày nghỉ sẽ dùng trong tháng: $willUsedDaysInMonth\n" .
 | 
						|
            //         " - Tổng ngày phép: $onleaveDaysTotal\n" .
 | 
						|
            //         " - Tổng ngày phép đã nghỉ: $usedOnleaveDaysTotal\n" .
 | 
						|
            //         " - Tổng ngày phép còn lại: $remainingOnleaveDays\n" .
 | 
						|
            //         " - Tổng ngày phép còn lại trong tháng: $remainingOnleaveDaysInMonth\n"
 | 
						|
            // );
 | 
						|
 | 
						|
            $month_data_status = 'ok';
 | 
						|
            $onleave_days_will_use = 0; // Ngày phép sẽ dùng trong tháng
 | 
						|
            $nopay_days_will_use = 0;   // Ngày ko phép sẽ dùng trong tháng
 | 
						|
 | 
						|
            // Ngày phép còn lại <= 0 (Hết phép) hoặc là nhân viên chưa chính thức
 | 
						|
            if ($remainingOnleaveDaysInMonth <= 0 || !$user->is_permanent) {
 | 
						|
                $hasInsufficientDays = true;
 | 
						|
                $month_data_status = 'no_days_left';
 | 
						|
                $onleave_days_will_use = 0;
 | 
						|
                $nopay_days_will_use = $monthData['days_requested'];
 | 
						|
 | 
						|
                // Message cảnh báo nghỉ ko phép
 | 
						|
                $monthMessage = $this->buildMonthlyLeaveMessage(
 | 
						|
                    $monthIndex,
 | 
						|
                    $maxDaysPerMonth,
 | 
						|
                    $monthData,
 | 
						|
                    $remainingOnleaveDaysInMonth,
 | 
						|
                    $onleaveDaysInMonth,
 | 
						|
                    $nopayDaysInMonth,
 | 
						|
                    $onleave_days_will_use,
 | 
						|
                    $nopay_days_will_use
 | 
						|
                );
 | 
						|
                $errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
 | 
						|
 | 
						|
                Log::debug("--- Hết phép trong tháng ---", [
 | 
						|
                    "Phep" => $onleave_days_will_use,
 | 
						|
                    "Khong Phep" => $nopay_days_will_use
 | 
						|
                ]);
 | 
						|
            }
 | 
						|
 | 
						|
            // Ngày phép còn lại < ngày yêu cầu (Không đủ phép)
 | 
						|
            else if ($remainingOnleaveDaysInMonth < $monthData['days_requested']) {
 | 
						|
                // Vượt limit
 | 
						|
                if ($willUsedDaysInMonth > $maxDaysPerMonth) {
 | 
						|
                    $hasInsufficientDays = true;
 | 
						|
                    $month_data_status = 'exceed_max_days';
 | 
						|
 | 
						|
                    // Phép còn lại > limit
 | 
						|
                    if ($remainingOnleaveDaysInMonth > $maxDaysPerMonth) {
 | 
						|
                        $onleave_days_will_use = $maxDaysPerMonth - $onleaveDaysInMonth;
 | 
						|
                        $nopay_days_will_use = $monthData['days_requested'] - $maxDaysPerMonth + $onleaveDaysInMonth;
 | 
						|
                    }
 | 
						|
                    // Phép còn lại < limit
 | 
						|
                    else {
 | 
						|
                        $onleave_days_will_use = $remainingOnleaveDaysInMonth;
 | 
						|
                        $nopay_days_will_use = $monthData['days_requested'] - $remainingOnleaveDaysInMonth;
 | 
						|
                    }
 | 
						|
 | 
						|
                    Log::debug("--- Không đủ phép trong tháng, vượt quá limit ---", [
 | 
						|
                        "Phep" => $onleave_days_will_use,
 | 
						|
                        "Khong Phep" => $nopay_days_will_use
 | 
						|
                    ]);
 | 
						|
                }
 | 
						|
                // Không vượt limit
 | 
						|
                else {
 | 
						|
                    $hasInsufficientDays = true;
 | 
						|
                    $month_data_status = 'insufficient_days';
 | 
						|
                    $onleave_days_will_use = $remainingOnleaveDaysInMonth;
 | 
						|
                    $nopay_days_will_use = $monthData['days_requested'] - $remainingOnleaveDaysInMonth;
 | 
						|
 | 
						|
                    Log::debug("--- Không đủ phép trong tháng, ko vượt limit ---", [
 | 
						|
                        "Phep" => $onleave_days_will_use,
 | 
						|
                        "Khong Phep" => $nopay_days_will_use
 | 
						|
                    ]);
 | 
						|
                }
 | 
						|
 | 
						|
                // Message cảnh báo nghỉ ko phép
 | 
						|
                $monthMessage = $this->buildMonthlyLeaveMessage(
 | 
						|
                    $monthIndex,
 | 
						|
                    $maxDaysPerMonth,
 | 
						|
                    $monthData,
 | 
						|
                    $remainingOnleaveDaysInMonth,
 | 
						|
                    $onleaveDaysInMonth,
 | 
						|
                    $nopayDaysInMonth,
 | 
						|
                    $onleave_days_will_use,
 | 
						|
                    $nopay_days_will_use
 | 
						|
                );
 | 
						|
                $errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
 | 
						|
            }
 | 
						|
 | 
						|
            // Ngày phép còn lại >= ngày yêu cầu (Đủ phép)
 | 
						|
            else {
 | 
						|
                // Vượt limit
 | 
						|
                if ($willUsedDaysInMonth > $maxDaysPerMonth) {
 | 
						|
                    $hasInsufficientDays = true;
 | 
						|
                    $month_data_status = 'exceed_max_days';
 | 
						|
                    $onleave_days_will_use = $maxDaysPerMonth - $onleaveDaysInMonth;
 | 
						|
                    $nopay_days_will_use = $willUsedDaysInMonth - $maxDaysPerMonth - $nopayDaysInMonth;
 | 
						|
 | 
						|
                    Log::debug("--- Đủ phép, vượt limit ---", [
 | 
						|
                        "Phep" => $onleave_days_will_use,
 | 
						|
                        "Khong Phep" => $nopay_days_will_use
 | 
						|
                    ]);
 | 
						|
                }
 | 
						|
                // Waiting ticket
 | 
						|
                else if ($monthData['days_requested'] + $remainingDaysInMonthIsUsed > $maxDaysPerMonth) {
 | 
						|
                    if ($remainingDaysInMonthIsUsed > 0) {
 | 
						|
                        $onleave_days_will_use = $maxDaysPerMonth - $remainingDaysInMonthIsUsed;
 | 
						|
                        $nopay_days_will_use = $monthData['days_requested'] - $onleave_days_will_use;
 | 
						|
                    } else {
 | 
						|
                        $onleave_days_will_use = $maxDaysPerMonth;
 | 
						|
                        $nopay_days_will_use = $monthData['days_requested'] - $maxDaysPerMonth;
 | 
						|
                    }
 | 
						|
                    $hasInsufficientDays = true;
 | 
						|
                    $month_data_status = 'exceed_max_days';
 | 
						|
 | 
						|
                    Log::debug("--- Đủ phép, Waiting ticket ---", [
 | 
						|
                        "Phep" => $onleave_days_will_use,
 | 
						|
                        "Khong Phep" => $nopay_days_will_use
 | 
						|
                    ]);
 | 
						|
                }
 | 
						|
                // Đủ phép
 | 
						|
                else {
 | 
						|
                    $onleave_days_will_use = $monthData['days_requested'];
 | 
						|
                    $nopay_days_will_use = 0;
 | 
						|
 | 
						|
                    Log::debug("--- Đủ phép ---", [
 | 
						|
                        "Phep" => $onleave_days_will_use,
 | 
						|
                        "Khong Phep" => $nopay_days_will_use
 | 
						|
                    ]);
 | 
						|
                }
 | 
						|
 | 
						|
                // Message cảnh báo nghỉ ko phép
 | 
						|
                $monthMessage = $this->buildMonthlyLeaveMessage(
 | 
						|
                    $monthIndex,
 | 
						|
                    $maxDaysPerMonth,
 | 
						|
                    $monthData,
 | 
						|
                    $remainingOnleaveDaysInMonth,
 | 
						|
                    $onleaveDaysInMonth,
 | 
						|
                    $nopayDaysInMonth,
 | 
						|
                    $onleave_days_will_use,
 | 
						|
                    $nopay_days_will_use
 | 
						|
                );
 | 
						|
                $errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
 | 
						|
 | 
						|
                $remainingOnleaveDaysInMonth = $monthData['days_requested'];
 | 
						|
            }
 | 
						|
 | 
						|
            $month_data = [
 | 
						|
                'year' => $monthData['year'],
 | 
						|
                'month' => $monthData['month'],
 | 
						|
                'total_leave_days_in_month' => $onleaveDaysTotal,                   // tổng số ngày phép
 | 
						|
                'total_leave_days_in_month_to_month' => $usedOnleaveDaysTotal,      // tổng ngày nghỉ có phép đã nghỉ
 | 
						|
                'remaining_days_in_month' => $remainingOnleaveDays,                 // số ngày phép còn lại
 | 
						|
                'days_used' => $onleaveDaysInMonth,                                 // tổng số ngày nghỉ có phép đã nghỉ ở tháng hiện tại
 | 
						|
                'days_used_without_pay' => $nopayDaysInMonth,                       // tổng số ngày nghỉ không phép đã nghỉ ở tháng hiện tại
 | 
						|
                'days_requested' => $monthData['days_requested'],                   // số ngày yêu cầu nghỉ của tháng
 | 
						|
                'remaining_days_in_month_remaining' => $remainingOnleaveDaysInMonth,
 | 
						|
                'days_will_use' => $onleave_days_will_use,                          //Số ngày phép sẽ sử dụng
 | 
						|
                'days_will_use_without_pay' => $nopay_days_will_use,                //Số ngày không phép sẽ sử dụng
 | 
						|
                'status' => $month_data_status,                                     // mặc định là ok
 | 
						|
            ];
 | 
						|
 | 
						|
            // Thêm thông tin tháng vào mảng kết quả
 | 
						|
            $monthsInfo[] = $month_data;
 | 
						|
            $monthIndex++;
 | 
						|
            $onleaveTmp += $onleave_days_will_use; // Cộng ngày phép dùng tạm trong tháng
 | 
						|
        }
 | 
						|
 | 
						|
        // Trả về kết quả tổng hợp
 | 
						|
        if ($hasInsufficientDays) {
 | 
						|
            return [
 | 
						|
                'success' => false,
 | 
						|
                'message' => $errorMessage,
 | 
						|
                'warning_type' => 'exceed_monthly_limit',
 | 
						|
                'months_info' => $monthsInfo
 | 
						|
            ];
 | 
						|
        }
 | 
						|
 | 
						|
        return [
 | 
						|
            'success' => true,
 | 
						|
            'message' => "Đủ ngày phép cho yêu cầu.",
 | 
						|
            'months_info' => $monthsInfo
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    //Tính tổng số ngày nghỉ có phép đến tháng hiện tại
 | 
						|
    private function getTotalLeaveDaysInMonthToMonth($user, int $year, int $month): float
 | 
						|
    {
 | 
						|
        return Notes::join('categories', function ($join) {
 | 
						|
            $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
						|
                ->where('categories.c_type', 'TIME_TYPE');
 | 
						|
        })
 | 
						|
            ->where('n_user_id', $user->id)
 | 
						|
            ->where('n_year', $year)
 | 
						|
            // ->where('n_month', "<=", $month)
 | 
						|
            ->where('n_reason', 'ONLEAVE')
 | 
						|
            ->sum('categories.c_value');
 | 
						|
    }
 | 
						|
 | 
						|
    private function getTotalLeaveDaysInMonth($user, int $year, int $month, ?bool $isAccept = false): float
 | 
						|
    {
 | 
						|
        $leaveDaysInfo = LeaveDays::where('ld_user_id', $user->id)
 | 
						|
            ->where('ld_year', $year)
 | 
						|
            ->first();
 | 
						|
 | 
						|
        $totalAllocated = 0;
 | 
						|
        // Xử lý gửi ticket sau tháng hiện tại
 | 
						|
        if ($leaveDaysInfo && $user->is_permanent) {
 | 
						|
            $currentMonth = Carbon::now()->month;
 | 
						|
            $totalAllocated = $leaveDaysInfo->ld_day_total;
 | 
						|
 | 
						|
            // Check có phải là nhân viên chính thức trong năm nay
 | 
						|
            $isProbationInYear = false;
 | 
						|
            if ($user->permanent_date && $user->permanent_date !== '0000-00-00') {
 | 
						|
                $permenantYear  = Carbon::parse($user->permanent_date)->year;
 | 
						|
 | 
						|
                if ($permenantYear === $year) {
 | 
						|
                    $isProbationInYear = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Nhân viên mới
 | 
						|
            if ($isProbationInYear) {
 | 
						|
                $permanentMonth = Carbon::parse($user->permanent_date)->month;
 | 
						|
                if ($month > $currentMonth) {
 | 
						|
                    $permanentCategory = Category::where('c_type', 'PERMANENT_ONLEAVE')->where('c_code', "PERMANENT")->first();
 | 
						|
                    $permanentDefault = (int) $permanentCategory->c_value; // Ngày phép khi thành nv chính thức
 | 
						|
 | 
						|
                    $totalAllocated = $month - ($permanentMonth - $permanentDefault);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Nhân viên cũ
 | 
						|
            else {
 | 
						|
                if ($month > $currentMonth) {
 | 
						|
                    $totalAllocated = $month;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $totalAllocated = $totalAllocated + $leaveDaysInfo->ld_additional_day + $leaveDaysInfo->ld_special_leave_day;
 | 
						|
        return $totalAllocated;
 | 
						|
    }
 | 
						|
 | 
						|
    private function groupLeaveRequestsByMonth(array $dataListPeriod): array
 | 
						|
    {
 | 
						|
        $requestMonths = [];
 | 
						|
        foreach ($dataListPeriod as $periodData) {
 | 
						|
            $date = Carbon::parse($periodData['date']);
 | 
						|
            $monthKey = $date->format('Y-m'); // YYYY-MM
 | 
						|
 | 
						|
            if (!isset($requestMonths[$monthKey])) {
 | 
						|
                $requestMonths[$monthKey] = [
 | 
						|
                    'year' => $date->year,
 | 
						|
                    'month' => $date->month,
 | 
						|
                    'days_requested' => 0,
 | 
						|
                    'days_used' => 0
 | 
						|
                ];
 | 
						|
            }
 | 
						|
 | 
						|
            // Tính số ngày yêu cầu trong tháng
 | 
						|
            $dayValue = ($periodData['period'] === 'ALL') ? 1.0 : 0.5;
 | 
						|
            $requestMonths[$monthKey]['days_requested'] += $dayValue;
 | 
						|
        }
 | 
						|
 | 
						|
        return $requestMonths;
 | 
						|
    }
 | 
						|
 | 
						|
    private function getUsedLeaveDaysInMonth($user, int $year, int $month, string $reason): float
 | 
						|
    {
 | 
						|
        return Notes::join('categories', function ($join) {
 | 
						|
            $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
						|
                ->where('categories.c_type', 'TIME_TYPE');
 | 
						|
        })
 | 
						|
            ->where('n_user_id', $user->id)
 | 
						|
            ->where('n_year', $year)
 | 
						|
            ->where('n_month', $month)
 | 
						|
            ->where('n_reason', $reason)
 | 
						|
            ->sum('categories.c_value');
 | 
						|
    }
 | 
						|
 | 
						|
    public function deleteTicket(Request $request)
 | 
						|
    {
 | 
						|
        $rules = [
 | 
						|
            'ticket_id' => 'required'
 | 
						|
        ];
 | 
						|
 | 
						|
        // Validate the request
 | 
						|
        $request->validate($rules);
 | 
						|
 | 
						|
        $user = auth('admins')->user();
 | 
						|
        $ticket_id = $request->input('ticket_id');
 | 
						|
        $ticket = Ticket::find($ticket_id);
 | 
						|
        if ($ticket) {
 | 
						|
            // $user->id == user_id of ticket ---> delete
 | 
						|
            if ($ticket->user_id == $user->id) {
 | 
						|
                $ticket->delete();
 | 
						|
                return response()->json(['message' => 'delete success', 'status' => true]);
 | 
						|
            } else {
 | 
						|
                return response()->json(['message' => 'You are committing an act of vandalism', 'status' => false]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return response()->json(['message' => 'Delete fail', 'status' => false]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function handleTicket(Request $request)
 | 
						|
    {
 | 
						|
        $rules = [
 | 
						|
            'ticket_id' => 'required',
 | 
						|
            'action' => 'required',
 | 
						|
        ];
 | 
						|
 | 
						|
        // Validate the request
 | 
						|
        $request->validate($rules);
 | 
						|
 | 
						|
        $ticket_id = $request->input('ticket_id');
 | 
						|
        $admin_note = $request->input('admin_note');
 | 
						|
        $action = $request->input('action'); // 'confirm' or 'refuse'
 | 
						|
        $admin = auth('admins')->user();
 | 
						|
        $ticket = Ticket::find($ticket_id);
 | 
						|
 | 
						|
        if (!$ticket || $ticket->status !== "WAITING") {
 | 
						|
            return response()->json(['message' => "Ticket not found", 'status' => false]);
 | 
						|
        }
 | 
						|
 | 
						|
        $dataMasterTypeNotes = CategoryController::getListMasterByType("REASON_NOTES");
 | 
						|
        $onleave = null;
 | 
						|
        $leaveWithoutPay = null;
 | 
						|
        $temporaryOnleave = null;
 | 
						|
 | 
						|
        if ($dataMasterTypeNotes) {
 | 
						|
            // get nghỉ phép, nghỉ không phép
 | 
						|
            $onleave = optional($dataMasterTypeNotes->where('c_code', 'ONLEAVE')->first())->c_code;
 | 
						|
            $leaveWithoutPay = optional($dataMasterTypeNotes->where('c_code', 'LEAVE_WITHOUT_PAY')->first())->c_code;
 | 
						|
            $temporaryOnleave = optional($dataMasterTypeNotes->where('c_code', 'TEMPORARY_ONLEAVE')->first())->c_code;
 | 
						|
        }
 | 
						|
 | 
						|
        if ($onleave == null || $leaveWithoutPay == null || $temporaryOnleave == null) {
 | 
						|
            return response()->json(['message' => "Data reason notes not found", 'status' => false]);
 | 
						|
        }
 | 
						|
 | 
						|
        if ($action == "confirm") {
 | 
						|
            $this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay, $temporaryOnleave);
 | 
						|
            return response()->json(['message' => "confirmed", 'status' => true]);
 | 
						|
        }
 | 
						|
 | 
						|
        if ($action == "refuse") {
 | 
						|
            $this->handleRefuseTicket($ticket, $admin, $admin_note);
 | 
						|
            return response()->json(['message' => "refused", 'status' => true]);
 | 
						|
        }
 | 
						|
 | 
						|
        return response()->json(['message' => "failed", 'status' => false]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function handleTicketEmail(Request $request)
 | 
						|
    {
 | 
						|
        $rules = [
 | 
						|
            'ticket_id' => 'required',
 | 
						|
            'action' => 'required',
 | 
						|
            'admin_email' => 'required' // Need Admin Email
 | 
						|
        ];
 | 
						|
 | 
						|
        // Validate the request
 | 
						|
        $request->validate($rules);
 | 
						|
 | 
						|
        $ticket_id = $request->input('ticket_id');
 | 
						|
        $admin_note = $request->input('admin_note');
 | 
						|
        $admin_email = $request->input('admin_email');
 | 
						|
        $action = $request->input('action'); // 'confirm' or 'refuse'
 | 
						|
        $admin = Admin::where('email', $admin_email)->first(); // Get admin by email not token
 | 
						|
        $ticket = Ticket::find($ticket_id);
 | 
						|
 | 
						|
        if (!$ticket || $ticket->status !== "WAITING") {
 | 
						|
            // No ticket found or already confirmed or refused
 | 
						|
            return redirect()->to(config('app.client_url') . '/404');
 | 
						|
        }
 | 
						|
 | 
						|
        $dataMasterTypeNotes = CategoryController::getListMasterByType("REASON_NOTES");
 | 
						|
        $onleave = null;
 | 
						|
        $leaveWithoutPay = null;
 | 
						|
        $temporaryOnleave = null;
 | 
						|
 | 
						|
        if ($dataMasterTypeNotes) {
 | 
						|
            // Get nghỉ phép, nghỉ không phép
 | 
						|
            $onleave = optional($dataMasterTypeNotes->where('c_code', 'ONLEAVE')->first())->c_code;
 | 
						|
            $leaveWithoutPay = optional($dataMasterTypeNotes->where('c_code', 'LEAVE_WITHOUT_PAY')->first())->c_code;
 | 
						|
            $temporaryOnleave = optional($dataMasterTypeNotes->where('c_code', 'TEMPORARY_ONLEAVE')->first())->c_code;
 | 
						|
        }
 | 
						|
 | 
						|
        // Không tìm được ngày phép, ko phép
 | 
						|
        if ($onleave == null || $leaveWithoutPay == null || $temporaryOnleave == null) {
 | 
						|
            return redirect()->to(config('app.client_url') . '/404');
 | 
						|
        }
 | 
						|
 | 
						|
        if ($action == "confirm") {
 | 
						|
            $this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay, $temporaryOnleave);
 | 
						|
            return redirect()->to(config('app.client_url') . '/tickets-management');
 | 
						|
        }
 | 
						|
 | 
						|
        if ($action == "refuse") {
 | 
						|
            $this->handleRefuseTicket($ticket, $admin, $admin_note);
 | 
						|
            return redirect()->to(config('app.client_url') . '/tickets-management');
 | 
						|
        }
 | 
						|
 | 
						|
        // Failed
 | 
						|
        return redirect()->to(config('app.client_url') . '/tickets-management');
 | 
						|
    }
 | 
						|
 | 
						|
    private function handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay, $temporaryOnleave)
 | 
						|
    {
 | 
						|
        $startDate = $ticket->start_date; //Start day
 | 
						|
        $startPeriod = $ticket->start_period; //The session begins
 | 
						|
        $endDate = $ticket->end_date; //End date
 | 
						|
        $endPeriod = $ticket->end_period; //Session ends
 | 
						|
        $type = $ticket->type;
 | 
						|
 | 
						|
        $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $startPeriod);
 | 
						|
        $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $endPeriod);
 | 
						|
        $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $type);
 | 
						|
 | 
						|
        $formattedStartDate = Carbon::createFromFormat('Y-m-d', $startDate)->format('d/m/Y');
 | 
						|
        $formattedEndDate = Carbon::createFromFormat('Y-m-d', $endDate)->format('d/m/Y');
 | 
						|
 | 
						|
        $user = Admin::find($ticket->user_id);
 | 
						|
 | 
						|
        if ($ticket->type == "ONLEAVE") {
 | 
						|
            $dataListPeriod = $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period);
 | 
						|
            $balanceCheckResult = $this->checkLeaveBalance($user, $dataListPeriod, null, true);
 | 
						|
            // dd($balanceCheckResult, $dataListPeriod);
 | 
						|
            $currentMonth = Carbon::now()->month;
 | 
						|
            if ($balanceCheckResult['success'] == false) {
 | 
						|
                if ($balanceCheckResult['months_info']) {
 | 
						|
                    foreach ($balanceCheckResult['months_info'] as $monthInfo) {
 | 
						|
                        // Lọc các ngày thuộc đúng tháng/năm này
 | 
						|
                        $daysInMonth = array_filter($dataListPeriod, function ($item) use ($monthInfo) {
 | 
						|
                            $date = \Carbon\Carbon::parse($item['date']);
 | 
						|
                            return $date->year == $monthInfo['year'] && $date->month == $monthInfo['month'];
 | 
						|
                        });
 | 
						|
 | 
						|
                        $daysWillUse = $monthInfo['days_will_use'] ?? 0;
 | 
						|
                        $daysWillUseWithoutPay = $monthInfo['days_will_use_without_pay'] ?? 0;
 | 
						|
                        // dd($daysWillUse,$daysWillUseWithoutPay,$daysInMonth);
 | 
						|
                        foreach ($daysInMonth as $item) {
 | 
						|
                            list($year, $month, $day) = explode('-', $item['date']);
 | 
						|
                            $period = $item['period'];
 | 
						|
                            $value = ($period === 'ALL') ? 1.0 : 0.5;
 | 
						|
 | 
						|
                            if ($period === 'ALL' && $daysWillUse == 0.5) {
 | 
						|
                                // Chỉ còn 0.5 phép, chia thành 2 bản ghi: 1 phép, 1 không phép
 | 
						|
                                // Ưu tiên phép cho buổi sáng (S), không phép cho buổi chiều (C)
 | 
						|
                                Notes::create([
 | 
						|
                                    'n_user_id' => $ticket->user_id,
 | 
						|
                                    'n_day' => $day,
 | 
						|
                                    'n_month' => $month,
 | 
						|
                                    'n_year' => $year,
 | 
						|
                                    'n_time_type' => 'S',
 | 
						|
                                    'n_reason' => $month > $currentMonth ? $temporaryOnleave :  $onleave,
 | 
						|
                                    'n_note' => $ticket->reason,
 | 
						|
                                    'ticket_id' => $ticket->id
 | 
						|
                                ]);
 | 
						|
                                Notes::create([
 | 
						|
                                    'n_user_id' => $ticket->user_id,
 | 
						|
                                    'n_day' => $day,
 | 
						|
                                    'n_month' => $month,
 | 
						|
                                    'n_year' => $year,
 | 
						|
                                    'n_time_type' => 'C',
 | 
						|
                                    'n_reason' => $month > $currentMonth ? $temporaryOnleave : $leaveWithoutPay,
 | 
						|
                                    'n_note' => $ticket->reason,
 | 
						|
                                    'ticket_id' => $ticket->id
 | 
						|
                                ]);
 | 
						|
                                $daysWillUse = 0;
 | 
						|
                                $daysWillUseWithoutPay -= 0.5;
 | 
						|
                            } elseif ($daysWillUse > 0) {
 | 
						|
                                // Dùng ngày phép trước
 | 
						|
                                $use = min($daysWillUse, $value);
 | 
						|
                                Notes::create([
 | 
						|
                                    'n_user_id' => $ticket->user_id,
 | 
						|
                                    'n_day' => $day,
 | 
						|
                                    'n_month' => $month,
 | 
						|
                                    'n_year' => $year,
 | 
						|
                                    'n_time_type' => $period,
 | 
						|
                                    'n_reason' => $month > $currentMonth ? $temporaryOnleave : $onleave,
 | 
						|
                                    'n_note' => $ticket->reason,
 | 
						|
                                    'ticket_id' => $ticket->id
 | 
						|
                                ]);
 | 
						|
                                $daysWillUse -= $use;
 | 
						|
                            } elseif ($daysWillUseWithoutPay > 0) {
 | 
						|
                                // Hết phép, chuyển sang không phép
 | 
						|
                                $use = min($daysWillUseWithoutPay, $value);
 | 
						|
                                Notes::create([
 | 
						|
                                    'n_user_id' => $ticket->user_id,
 | 
						|
                                    'n_day' => $day,
 | 
						|
                                    'n_month' => $month,
 | 
						|
                                    'n_year' => $year,
 | 
						|
                                    'n_time_type' => $period,
 | 
						|
                                    'n_reason' => $month > $currentMonth ? $temporaryOnleave : $leaveWithoutPay,
 | 
						|
                                    'n_note' => $ticket->reason,
 | 
						|
                                    'ticket_id' => $ticket->id
 | 
						|
                                ]);
 | 
						|
                                $daysWillUseWithoutPay -= $use;
 | 
						|
                            }
 | 
						|
                            // Nếu cả hai đều hết thì thôi, không tạo nữa
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                //Đủ phép
 | 
						|
                foreach ($dataListPeriod as $result) {
 | 
						|
                    list($year, $month, $day) = explode('-', $result['date']);
 | 
						|
                    Notes::create([
 | 
						|
                        'n_user_id' => $ticket->user_id,
 | 
						|
                        'n_day' => $day,
 | 
						|
                        'n_month' => $month,
 | 
						|
                        'n_year' => $year,
 | 
						|
                        'n_time_type' => $result['period'],
 | 
						|
                        'n_reason' => $month > $currentMonth ? $temporaryOnleave : $onleave,
 | 
						|
                        'n_note' => $ticket->reason,
 | 
						|
                        'ticket_id' => $ticket->id
 | 
						|
                    ]);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // $yearCheck = Carbon::parse($endDate)->year;
 | 
						|
            // // Check giá trị ld_day_total của bảng leave_days thuộc user id đó với giá trị của list item note trong bảng notes của user id đó
 | 
						|
            // $leaveDaysInfo = LeaveDays::where('ld_user_id', $ticket->user_id)
 | 
						|
            //     ->where('ld_year', $yearCheck)
 | 
						|
            //     ->first();
 | 
						|
            // if ($leaveDaysInfo) {
 | 
						|
            //     // Tính tổng số ngày nghỉ có phép đã sử dụng trong năm
 | 
						|
            //     $totalUsedLeaveDays = Notes::join('categories', function ($join) {
 | 
						|
            //         $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
						|
            //             ->where('categories.c_type', 'TIME_TYPE');
 | 
						|
            //     })
 | 
						|
            //         ->where('n_user_id', $ticket->user_id)
 | 
						|
            //         ->where('n_year', $yearCheck)
 | 
						|
            //         ->where('n_reason', 'ONLEAVE')
 | 
						|
            //         ->sum('categories.c_value');
 | 
						|
 | 
						|
            //     // Tính tổng số ngày phép được cấp
 | 
						|
            //     $totalAllocatedDays = $leaveDaysInfo->ld_day_total +
 | 
						|
            //         $leaveDaysInfo->ld_additional_day +
 | 
						|
            //         $leaveDaysInfo->ld_special_leave_day;
 | 
						|
 | 
						|
            //     // Tính số ngày vượt quá và làm tròn lên
 | 
						|
            //     $excessDays = $totalUsedLeaveDays - $totalAllocatedDays;
 | 
						|
            //     $roundedExcessDays = ceil($excessDays); // Làm tròn lên số nguyên gần nhất
 | 
						|
 | 
						|
            //     // Kiểm tra nếu số ngày đã sử dụng vượt quá số ngày được cấp
 | 
						|
            //     if ($roundedExcessDays > 0) {
 | 
						|
            //         Log::warning("User ID: {$ticket->user_id} has used more leave days ({$totalUsedLeaveDays}) than allocated ({$totalAllocatedDays})");
 | 
						|
 | 
						|
            //         // Cập nhật cột ld_day_total với số ngày đã làm tròn
 | 
						|
            //         $leaveDaysInfo->ld_day_total += $roundedExcessDays;
 | 
						|
            //         $leaveDaysInfo->save();
 | 
						|
 | 
						|
            //         Log::info("Updated leave days for User ID: {$ticket->user_id}. Added {$roundedExcessDays} days (rounded from {$excessDays})");
 | 
						|
            //     }
 | 
						|
            // }
 | 
						|
        } else if ($ticket->type == "WFH") {
 | 
						|
            $dataListPeriod = $this->getAllPeriod($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period);
 | 
						|
            foreach ($dataListPeriod as $result) {
 | 
						|
                list($year, $month, $day) = explode('-', $result['date']);
 | 
						|
                Notes::create([
 | 
						|
                    'n_user_id' => $ticket->user_id,
 | 
						|
                    'n_day' => $day,
 | 
						|
                    'n_month' => $month,
 | 
						|
                    'n_year' => $year,
 | 
						|
                    'n_time_type' => $result['period'],
 | 
						|
                    'n_reason' => $ticket->type,
 | 
						|
                    'n_note' => $ticket->reason,
 | 
						|
                    'ticket_id' => $ticket->id
 | 
						|
                ]);
 | 
						|
 | 
						|
 | 
						|
                //WFH - start tracking
 | 
						|
                $type =  $result['period'];
 | 
						|
                $date = Carbon::create($year, $month, $day)->setTimezone(env('TIME_ZONE'));
 | 
						|
 | 
						|
                //Default: ALL
 | 
						|
                $start = $date->copy()->setTime(7, 30, 0);
 | 
						|
                $end = $date->copy()->setTime(17, 0, 0);
 | 
						|
 | 
						|
                if ($type == 'S') {
 | 
						|
                    $end = $date->copy()->setTime(11, 30, 0);
 | 
						|
                } else if ($type == 'C') {
 | 
						|
                    $start = $date->copy()->setTime(13, 0, 0);
 | 
						|
                }
 | 
						|
 | 
						|
                Tracking::insert([
 | 
						|
                    [
 | 
						|
                        'name' => $user->name,
 | 
						|
                        'user_id' => $user->id,
 | 
						|
                        'status' => 'check in',
 | 
						|
                        'time_string' => $start->format('Y-m-d H:i:s'),
 | 
						|
                        'created_at' =>  $start->setTimezone(env('TIME_ZONE'))
 | 
						|
                    ],
 | 
						|
                    [
 | 
						|
                        'name' => $user->name,
 | 
						|
                        'user_id' => $user->id,
 | 
						|
                        'status' => 'check out',
 | 
						|
                        'time_string' => $end->format('Y-m-d H:i:s'),
 | 
						|
                        'created_at' => $end->setTimezone(env('TIME_ZONE'))
 | 
						|
                    ]
 | 
						|
                ]);
 | 
						|
                //WFH - end tracking
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        $ticket['updated_by'] = $admin->name;
 | 
						|
        $ticket['admin_note'] = $admin_note;
 | 
						|
        $ticket['status'] = 'CONFIRMED';
 | 
						|
        $ticket->save();
 | 
						|
 | 
						|
        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
 | 
						|
        $this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
 | 
						|
 | 
						|
        // Send notification email to users
 | 
						|
        $data = array(
 | 
						|
            "email_template" => "email.notification_tickets_user",
 | 
						|
            "user_name" => $user->name,
 | 
						|
            "email" => $user->email,
 | 
						|
            "name" => $admin->name, //name admin duyệt
 | 
						|
            "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
						|
            "type" => $dataMasterType->c_name,
 | 
						|
            "note" => $ticket->reason,
 | 
						|
            "admin_note" => $admin_note,
 | 
						|
            "link" => "/tickets", //link đến page admin
 | 
						|
            "status" => "confirmed",
 | 
						|
            "subject" => "[Ticket response] Ticket From " . $admin->name
 | 
						|
        );
 | 
						|
        Mail::to($user->email)->send(new TicketMail($data));
 | 
						|
    }
 | 
						|
 | 
						|
    private function handleRefuseTicket($ticket, $admin, $admin_note)
 | 
						|
    {
 | 
						|
        $startDate = $ticket->start_date; //Start day
 | 
						|
        $startPeriod = $ticket->start_period; //The session begins
 | 
						|
        $endDate = $ticket->end_date; //End date
 | 
						|
        $endPeriod = $ticket->end_period; //Session ends
 | 
						|
        $type = $ticket->type;
 | 
						|
 | 
						|
        $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $startPeriod);
 | 
						|
        $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $endPeriod);
 | 
						|
        $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $type);
 | 
						|
 | 
						|
        $formattedStartDate = Carbon::createFromFormat('Y-m-d', $startDate)->format('d/m/Y');
 | 
						|
        $formattedEndDate = Carbon::createFromFormat('Y-m-d', $endDate)->format('d/m/Y');
 | 
						|
 | 
						|
        $user = Admin::find($ticket->user_id);
 | 
						|
 | 
						|
        $ticket['updated_by'] = $admin->name;
 | 
						|
        $ticket['admin_note'] = $admin_note;
 | 
						|
        $ticket['status'] = 'REFUSED';
 | 
						|
        $ticket->save();
 | 
						|
 | 
						|
        $data = array(
 | 
						|
            "email_template" => "email.notification_tickets_user",
 | 
						|
            "user_name" => $user->name,
 | 
						|
            "email" => $user->email,
 | 
						|
            "name" => $admin->name, //name admin duyệt
 | 
						|
            "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
						|
            "type" => $dataMasterType->c_name,
 | 
						|
            "note" => $ticket->reason,
 | 
						|
            "admin_note" => $admin_note,
 | 
						|
            "link" => "/tickets", //link đến page admin
 | 
						|
            "status" => "refused",
 | 
						|
            "subject" => "[Ticket response] Ticket From " . $admin->name
 | 
						|
        );
 | 
						|
        Mail::to($user->email)->send(new TicketMail($data));
 | 
						|
    }
 | 
						|
 | 
						|
    private function getAllPeriodNew($startDate, $startPeriod, $endDate, $endPeriod)
 | 
						|
    {
 | 
						|
        // Đảm bảo $startDate và $endDate là đối tượng Carbon
 | 
						|
        if (!($startDate instanceof Carbon)) {
 | 
						|
            $startDate = Carbon::parse($startDate);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!($endDate instanceof Carbon)) {
 | 
						|
            $endDate = Carbon::parse($endDate);
 | 
						|
        }
 | 
						|
 | 
						|
        // Create an array to contain the results
 | 
						|
        $results = [];
 | 
						|
 | 
						|
        // Use CarbonPeriod to create a period from the start date to the end date
 | 
						|
        $period = CarbonPeriod::create($startDate, $endDate);
 | 
						|
 | 
						|
        $time_type = Category::where('c_type', 'TIME_TYPE')->get()->keyBy('c_code');
 | 
						|
        $morning = $time_type->get('S');
 | 
						|
        $afternoon = $time_type->get('C');
 | 
						|
        $all_day = $time_type->get('ALL');
 | 
						|
 | 
						|
        // Get all Saturday work schedules and sort them by date in descending order
 | 
						|
        $saturday_work_schedules = Category::where('c_type', 'SATURDAY_WORK_SCHEDULE')
 | 
						|
            ->get()
 | 
						|
            ->sortByDesc(function ($item) {
 | 
						|
                // Parse the date string from c_code to a Carbon instance for proper comparison
 | 
						|
                return Carbon::createFromFormat('d-m-Y', $item->c_code);
 | 
						|
            });
 | 
						|
        // get day work special
 | 
						|
        $day_work_special = Category::where('c_type', 'DAY_WORK_SPECIAL')
 | 
						|
            ->get()
 | 
						|
            ->sortByDesc(function ($item) {
 | 
						|
                return Carbon::createFromFormat('d-m-Y', $item->c_code);
 | 
						|
            });
 | 
						|
 | 
						|
 | 
						|
        // Get the most recent schedule date (first item after sorting)
 | 
						|
        $latest_schedule = $saturday_work_schedules->first();
 | 
						|
        $latestScheduleDate = Carbon::createFromFormat('d-m-Y', $latest_schedule->c_code);
 | 
						|
 | 
						|
        if (!$morning || !$afternoon || !$all_day) {
 | 
						|
            // Handle error: TIME_TYPE categories not found
 | 
						|
            Log::error("TIME_TYPE categories (S, C, ALL) not found in database.");
 | 
						|
            return []; // Return empty or throw exception
 | 
						|
        }
 | 
						|
 | 
						|
        $special_dates = [];
 | 
						|
        foreach ($day_work_special as $item) {
 | 
						|
            $special_dates[] = Carbon::createFromFormat('d-m-Y', $item->c_code)->toDateString();
 | 
						|
        }
 | 
						|
        foreach ($period as $date) {
 | 
						|
            // Check phải ngày thứ 7 đặc biệt thì tính như ngày bình thường
 | 
						|
            if (in_array($date->toDateString(), $special_dates)) {
 | 
						|
            } else {
 | 
						|
                // Check if the current day is a Saturday
 | 
						|
                if ($date->dayOfWeek === Carbon::SATURDAY) {
 | 
						|
                    if ($latest_schedule) {
 | 
						|
                        $weeksDifference = $latestScheduleDate->startOfDay()->diffInWeeks($date->copy()->startOfDay());
 | 
						|
                        $isSaturdayWorkDay = ($weeksDifference % 2 === 0);
 | 
						|
 | 
						|
                        // echo $date->toDateString() . ' - ' . ($isSaturdayWorkDay ? 'Làm việc' : 'Nghỉ') . "<br>";
 | 
						|
                    }
 | 
						|
 | 
						|
                    if ($isSaturdayWorkDay) {
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => "S"];
 | 
						|
                    }
 | 
						|
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                // Skip Sundays entirely
 | 
						|
                else if ($date->dayOfWeek === Carbon::SUNDAY) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if ($date->isSameDay($startDate)) {
 | 
						|
                //If the start date is morning, add afternoon
 | 
						|
                if ($startDate->isSameDay($endDate)) { // Nghỉ trong cùng 1 ngày
 | 
						|
                    if ($startPeriod == $endPeriod) { // Cùng 1 buổi (S hoặc C)
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $startPeriod];
 | 
						|
                    } else { // Khác buổi (S đến C) -> cả ngày
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                    }
 | 
						|
                } else { // Ngày bắt đầu khác ngày kết thúc
 | 
						|
                    if ($startPeriod == $morning->c_code) { // Bắt đầu từ sáng -> tính cả ngày
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                    } else { // Bắt đầu từ chiều -> tính buổi chiều
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $startPeriod]; // Là $afternoon->c_code
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } elseif ($date->isSameDay($endDate)) { // Ngày kết thúc (khác ngày bắt đầu)
 | 
						|
                if ($endPeriod == $afternoon->c_code) { // Kết thúc vào buổi chiều -> tính cả ngày
 | 
						|
                    $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                } else { // Kết thúc vào buổi sáng -> tính buổi sáng
 | 
						|
                    $results[] = ['date' => $date->toDateString(), 'period' => $endPeriod]; // Là $morning->c_code
 | 
						|
                }
 | 
						|
            } else { // Những ngày ở giữa
 | 
						|
                $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Returns results
 | 
						|
        return $results;
 | 
						|
    }
 | 
						|
 | 
						|
    private function getAllPeriod($startDate, $startPeriod, $endDate, $endPeriod)
 | 
						|
    {
 | 
						|
        //Create an array to contain the results
 | 
						|
        $results = [];
 | 
						|
 | 
						|
        //Use CarbonPeriod to create a period from the start date to the end date
 | 
						|
        $period = CarbonPeriod::create($startDate, $endDate);
 | 
						|
 | 
						|
        $time_type = Category::where('c_type', 'TIME_TYPE')->get();
 | 
						|
        $morning = null;
 | 
						|
        $afternoon = null;
 | 
						|
        $all_day = null;
 | 
						|
 | 
						|
        foreach ($time_type as $item) {
 | 
						|
            switch ($item->c_code) {
 | 
						|
                case 'S':
 | 
						|
                    $morning = $item;
 | 
						|
                    break;
 | 
						|
                case 'C':
 | 
						|
                    $afternoon = $item;
 | 
						|
                    break;
 | 
						|
                case 'ALL':
 | 
						|
                    $all_day = $item;
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        foreach ($period as $date) {
 | 
						|
            //If it is the start date
 | 
						|
            if ($date->isSameDay($startDate)) {
 | 
						|
                //Add start session
 | 
						|
                //If the start date is morning, add afternoon
 | 
						|
                if ($startDate == $endDate) {
 | 
						|
                    if ($startPeriod == $endPeriod) {
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $startPeriod];
 | 
						|
                    } else {
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    if ($startPeriod == $morning->c_code) {
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                    } else {
 | 
						|
                        $results[] = ['date' => $date->toDateString(), 'period' => $startPeriod];
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } elseif ($date->isSameDay($endDate)) {
 | 
						|
                //If it is the end date
 | 
						|
                //If the end session is afternoon, add morning first
 | 
						|
 | 
						|
                if ($endPeriod == $afternoon->c_code) {
 | 
						|
                    $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                } else {
 | 
						|
                    $results[] = ['date' => $date->toDateString(), 'period' => $endPeriod];
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                //Add both sessions for days between intervals
 | 
						|
                $results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
 | 
						|
                // $results[] = ['date' => $date->toDateString(), 'period' => 'afternoon'];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //Returns results
 | 
						|
        return $results;
 | 
						|
    }
 | 
						|
 | 
						|
    private function buildMonthlyLeaveMessage(
 | 
						|
        $index,
 | 
						|
        $max,
 | 
						|
        $monthData,
 | 
						|
        $totalLeave,
 | 
						|
        $usedLeave,
 | 
						|
        $usedNoPay,
 | 
						|
        $willUseLeave,
 | 
						|
        $willUseNoPay
 | 
						|
    ): string {
 | 
						|
        $message = "";
 | 
						|
 | 
						|
        if ($index === 0) {
 | 
						|
            $showMonth = $monthData['month'] > Carbon::now()->month ? $monthData['month'] : Carbon::now()->month;
 | 
						|
            $message .= "* Quy định: mỗi tháng được nghỉ tối đa {$max} ngày phép\n";
 | 
						|
            $message .= "- Bạn có: {$totalLeave} ngày phép (Tính tới {$showMonth}/{$monthData['year']})\n\n";
 | 
						|
        }
 | 
						|
 | 
						|
        // Hiển thị cộng phép nếu gửi ticket trong tương lai
 | 
						|
        $monthValue = $monthData['year'] . '-' . sprintf('%02d', $monthData['month']);
 | 
						|
        $currentMonth = date('Y-m');
 | 
						|
        if ($monthValue > $currentMonth && $index !== 0) {
 | 
						|
            $message .= "* Bạn được cộng 1 phép\n";
 | 
						|
        }
 | 
						|
 | 
						|
        // In mỗi tháng
 | 
						|
        $message .= "Tháng {$monthData['month']}/{$monthData['year']}:\n";
 | 
						|
        if ($usedLeave > 0 || $usedNoPay > 0) {
 | 
						|
            $message .= "  - Bạn đã sử dụng: ";
 | 
						|
            $usedParts = [];
 | 
						|
            if ($usedLeave > 0) $usedParts[] = "{$usedLeave} phép";
 | 
						|
            if ($usedNoPay > 0) $usedParts[] = "{$usedNoPay} không phép";
 | 
						|
            $message .= implode(', ', $usedParts) . "\n";
 | 
						|
        }
 | 
						|
 | 
						|
        if ($willUseLeave > 0 || $willUseNoPay > 0) {
 | 
						|
            $message .= "  - Dự kiến bạn sẽ sử dụng: ";
 | 
						|
            $usedParts = [];
 | 
						|
            if ($willUseLeave > 0) $usedParts[] = "{$willUseLeave} phép";
 | 
						|
            if ($willUseNoPay > 0) $usedParts[] = "{$willUseNoPay} không phép";
 | 
						|
            $message .= implode(', ', $usedParts);
 | 
						|
        }
 | 
						|
 | 
						|
        return $message;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Tính tổng số ngày nghỉ từ mảng các khoảng thời gian.
 | 
						|
     * 'ALL' = 1 ngày, 'S'/'C' = 0.5 ngày.
 | 
						|
     *
 | 
						|
     * @param array $dataListPeriod Mảng các khoảng thời gian nghỉ [['date' => 'Y-m-d', 'period' => 'ALL|S|C'], ...]
 | 
						|
     * @return float Tổng số ngày nghỉ
 | 
						|
     */
 | 
						|
    private function calculateTotalLeaveDays(array $dataListPeriod): float
 | 
						|
    {
 | 
						|
        $totalDays = 0.0;
 | 
						|
 | 
						|
        foreach ($dataListPeriod as $periodData) {
 | 
						|
            if (isset($periodData['period'])) {
 | 
						|
                switch ($periodData['period']) {
 | 
						|
                    case 'ALL':
 | 
						|
                        $totalDays += 1.0;
 | 
						|
                        break;
 | 
						|
                    case 'S': // Buổi sáng
 | 
						|
                    case 'C': // Buổi chiều
 | 
						|
                        $totalDays += 0.5;
 | 
						|
                        break;
 | 
						|
                        // Có thể thêm default case để xử lý lỗi nếu cần
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $totalDays;
 | 
						|
    }
 | 
						|
 | 
						|
    public function updateOldData(int $month, int $year)
 | 
						|
    {
 | 
						|
        LeaveDays::where('ld_year', $year)
 | 
						|
            ->update(['ld_day_total' => $month]);
 | 
						|
 | 
						|
        $users = Admin::all();
 | 
						|
 | 
						|
        foreach ($users as $user) {
 | 
						|
            $leaveDay = LeaveDays::where('ld_user_id', $user->id)
 | 
						|
                ->where('ld_year', $year)
 | 
						|
                ->first();
 | 
						|
            $notes = Notes::where('n_user_id', $user->id)
 | 
						|
                ->where('n_year', $year)
 | 
						|
                ->where('n_reason', 'ONLEAVE')
 | 
						|
                ->orderBy('n_month')
 | 
						|
                ->orderBy('n_day')
 | 
						|
                ->get()
 | 
						|
                ->groupBy('n_month');
 | 
						|
 | 
						|
            $onleaveDaysTotal = $leaveDay->ld_additional_day;
 | 
						|
 | 
						|
            $previousYearData = LeaveDays::where('ld_user_id', $user->id)
 | 
						|
                ->where('ld_year', $year - 1)
 | 
						|
                ->first();
 | 
						|
 | 
						|
            $ld_additional_day = 0;
 | 
						|
            $ld_note = '';
 | 
						|
 | 
						|
            if ($previousYearData) {
 | 
						|
                $ld_additional_day = $previousYearData->ld_day_total + $previousYearData->ld_additional_day;
 | 
						|
                $totalLeaveDaysByMonth = Notes::join('categories', function ($join) {
 | 
						|
                    $join->on('notes.n_time_type', '=', 'categories.c_code')
 | 
						|
                        ->where('categories.c_type', 'TIME_TYPE');
 | 
						|
                })
 | 
						|
                    ->select(
 | 
						|
                        DB::raw('notes.n_user_id as n_user_id'),
 | 
						|
                        DB::raw('notes.n_year as year'),
 | 
						|
                        DB::raw('SUM(categories.c_value) as leave_days')
 | 
						|
                    )
 | 
						|
                    ->where('notes.n_year', $year - 1)
 | 
						|
                    ->where('notes.n_user_id', $user->id)
 | 
						|
                    ->where('notes.n_reason', 'ONLEAVE')
 | 
						|
                    ->groupBy(DB::raw('notes.n_year'))
 | 
						|
                    ->first();
 | 
						|
                if ($totalLeaveDaysByMonth) {
 | 
						|
                    $ld_additional_day = $ld_additional_day - $totalLeaveDaysByMonth->leave_days;
 | 
						|
                    if ($ld_additional_day < 0) {
 | 
						|
                        $ld_additional_day = 0;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if ($ld_additional_day > 0) {
 | 
						|
                    $ld_note = "Cộng " . $ld_additional_day . " ngày phép tồn năm trước. \n";
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            for ($i = 1; $i <= $month; $i++) {
 | 
						|
                // Giả lập cộng phép
 | 
						|
                $onleaveDaysTotal++;
 | 
						|
                // $tmpOnleaveDaysTotal = $onleaveDaysTotal;
 | 
						|
 | 
						|
                $onleaveDaysInMonth = 0;
 | 
						|
                $nopayDaysInMonth = 0;
 | 
						|
 | 
						|
                if ($notes->has($i)) {
 | 
						|
                    foreach ($notes[$i] as $note) {
 | 
						|
                        $onleaveDaysInMonth += $note->n_time_type == 'ALL' ? 1.0 : 0.5;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if ($onleaveDaysInMonth > $onleaveDaysTotal) {
 | 
						|
                        $nopayDaysInMonth = $onleaveDaysInMonth - $onleaveDaysTotal;
 | 
						|
                        $onleaveDaysTotal = 0;
 | 
						|
                    } else {
 | 
						|
                        $onleaveDaysTotal -= $onleaveDaysInMonth;
 | 
						|
                    }
 | 
						|
 | 
						|
                    // Xử lý cập nhật lại các note có phép thành không phép
 | 
						|
                    if ($nopayDaysInMonth > 0) {
 | 
						|
                        $revertNotes = $notes->get($i, collect())->reverse();
 | 
						|
                        $nopayDaysUpdated = 0;
 | 
						|
 | 
						|
                        foreach ($revertNotes as $note) {
 | 
						|
                            if ($note->n_time_type == 'ALL') {
 | 
						|
                                if ($nopayDaysInMonth - $nopayDaysUpdated == 0.5) {
 | 
						|
                                    Notes::create([
 | 
						|
                                        'n_user_id' => $user->id,
 | 
						|
                                        'n_day' => $note->n_day,
 | 
						|
                                        'n_month' => $note->n_month,
 | 
						|
                                        'n_year' => $note->n_year,
 | 
						|
                                        'n_time_type' => 'S',
 | 
						|
                                        'n_reason' => 'ONLEAVE',
 | 
						|
                                        'n_note' => $note->n_note,
 | 
						|
                                        'ticket_id' => $note->ticket_id
 | 
						|
                                    ]);
 | 
						|
                                    Notes::create([
 | 
						|
                                        'n_user_id' => $user->id,
 | 
						|
                                        'n_day' => $note->n_day,
 | 
						|
                                        'n_month' => $note->n_month,
 | 
						|
                                        'n_year' => $note->n_year,
 | 
						|
                                        'n_time_type' => 'C',
 | 
						|
                                        'n_reason' => 'LEAVE_WITHOUT_PAY',
 | 
						|
                                        'n_note' => $note->n_note,
 | 
						|
                                        'ticket_id' => $note->ticket_id
 | 
						|
                                    ]);
 | 
						|
 | 
						|
                                    $note->delete();
 | 
						|
                                    break;
 | 
						|
                                }
 | 
						|
 | 
						|
                                $nopayDaysUpdated += 1.0;
 | 
						|
                                $note->update([
 | 
						|
                                    'n_reason' => "LEAVE_WITHOUT_PAY"
 | 
						|
                                ]);
 | 
						|
                            } else {
 | 
						|
                                $nopayDaysUpdated += 0.5;
 | 
						|
                                $note->update([
 | 
						|
                                    'n_reason' => "LEAVE_WITHOUT_PAY"
 | 
						|
                                ]);
 | 
						|
                            }
 | 
						|
 | 
						|
                            if ($nopayDaysUpdated >= $nopayDaysInMonth) {
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                // Log thông kê sau mỗi tháng
 | 
						|
                // Log::debug(
 | 
						|
                //     "📊 Thống kê ngày phép Tháng {$i}:\n" .
 | 
						|
                //         " - Tổng phép đầu tháng: $tmpOnleaveDaysTotal\n" .
 | 
						|
                //         " - Có phép: $onleaveDaysInMonth\n" .
 | 
						|
                //         " - Không phép: $nopayDaysInMonth\n" .
 | 
						|
                //         " - Tổng phép cuối tháng: $onleaveDaysTotal\n"
 | 
						|
                // );
 | 
						|
            }
 | 
						|
 | 
						|
            $leaveDay->ld_note = $ld_note;
 | 
						|
            $leaveDay->save();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |