diff --git a/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php b/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php index 7bde71d..10b57f0 100644 --- a/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php +++ b/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php @@ -2,9 +2,7 @@ namespace Modules\Admin\app\Http\Controllers; -use App\Helper\Cache\CurrentMonthTimekeeping; use App\Http\Controllers\Controller; -use App\Mail\ContactMail; use App\Mail\TicketMail; use App\Models\Notes; use App\Traits\AnalyzeData; @@ -22,7 +20,6 @@ use Modules\Admin\app\Models\Ticket; use Modules\Admin\app\Models\Tracking; use Illuminate\Support\Facades\Log; use App\Models\LeaveDays; -use Illuminate\Http\JsonResponse; use App\Models\Admin as UserModel; class TicketController extends Controller @@ -194,7 +191,7 @@ class TicketController extends Controller public function createTicket(Request $request) { - // Define validation rules + // Validate input $rules = [ 'start_date' => 'required|date', 'start_period' => 'required|string', @@ -202,12 +199,9 @@ class TicketController extends Controller 'end_period' => 'required|string', 'type' => 'required|string', ]; - - // Validate the request $request->validate($rules); - // return $request; - // Get data from request + // Get input data $startDate = $request->input('start_date'); $startPeriod = $request->input('start_period'); $endDate = $request->input('end_date'); @@ -215,21 +209,72 @@ class TicketController extends Controller $type = $request->input('type'); $reason = $request->input('reason'); $isAccept = $request->input('is_accept') ?? false; - $user = auth('admins')->user(); // user create ticket + $user = auth('admins')->user(); $start_date = Carbon::create($startDate)->setTimezone(env('TIME_ZONE')); $end_date = Carbon::create($endDate)->setTimezone(env('TIME_ZONE')); - // Get mảng ngày nghỉ $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ệ.'); } - // --- Chỉ kiểm tra ngày phép khi loại là ONLEAVE --- + // 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 thông tin tickets nghỉ phép đang ở trạng thái WAITING - $ticketsWaiting = Ticket::where('user_id', $user->id)->where('status', 'WAITING')->whereIn('type', ['ONLEAVE']) + // 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) { @@ -238,100 +283,6 @@ class TicketController extends Controller } } - $ticketsWaitingWFH = Ticket::where('user_id', $user->id)->where('status', 'WAITING')->whereIn('type', ['WFH']) - ->get(); - $dataListPeriodWaitingWFH = []; - if ($ticketsWaitingWFH->count() > 0) { - foreach ($ticketsWaitingWFH as $ticket) { - $dataListPeriodWaitingWFH = array_merge($dataListPeriodWaitingWFH, $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period)); - } - } - - // Lấy thông tin tickets nghỉ phép đang ở trạng thái CONFIRMED - $ticketsConfirmed = Ticket::where('user_id', $user->id) - ->where('status', 'CONFIRMED') - ->whereIn('type', ['ONLEAVE', 'WFH']) - ->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(); - - $dataListPeriodConfirmed = []; - if ($ticketsConfirmed->count() > 0) { - foreach ($ticketsConfirmed as $ticket) { - $dataListPeriodConfirmed = array_merge($dataListPeriodConfirmed, $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period)); - } - } - // dd($dataListPeriodConfirmed,$ticketsConfirmed,$start_date->toDateString(),$end_date->toDateString()); - // Chuyển đổi mảng đa chiều thành mảng chuỗi để có thể so sánh - $periodStrings = []; - $waitingPeriodStrings = []; - $waitingPeriodStringsWFH = []; - $confirmedPeriodStrings = []; - - foreach ($dataListPeriod as $period) { - if ($period['period'] == 'ALL') { - $periodStrings[] = $period['date'] . '_S'; - $periodStrings[] = $period['date'] . '_C'; - } else { - $periodStrings[] = $period['date'] . '_' . $period['period']; - } - } - - foreach ($dataListPeriodWaiting as $period) { - if ($period['period'] == 'ALL') { - $waitingPeriodStrings[] = $period['date'] . '_S'; - $waitingPeriodStrings[] = $period['date'] . '_C'; - } else { - $waitingPeriodStrings[] = $period['date'] . '_' . $period['period']; - } - } - - foreach ($dataListPeriodWaitingWFH as $period) { - if ($period['period'] == 'ALL') { - $waitingPeriodStringsWFH[] = $period['date'] . '_S'; - $waitingPeriodStringsWFH[] = $period['date'] . '_C'; - } else { - $waitingPeriodStringsWFH[] = $period['date'] . '_' . $period['period']; - } - } - - foreach ($dataListPeriodConfirmed as $period) { - if ($period['period'] == 'ALL') { - $confirmedPeriodStrings[] = $period['date'] . '_S'; - $confirmedPeriodStrings[] = $period['date'] . '_C'; - } else { - $confirmedPeriodStrings[] = $period['date'] . '_' . $period['period']; - } - } - - // Kiểm tra xem có sự trùng lặp giữa request mới và tickets đang chờ duyệt - if (count(array_intersect($periodStrings, $waitingPeriodStrings)) > 0) { - return AbstractController::ResultError('Đã có ticket đang chờ duyệt trong thời gian này, không thể tạo ticket mới!'); - } - - // Kiểm tra xem có sự trùng lặp giữa request mới và tickets đang chờ duyệt WFH - if (count(array_intersect($periodStrings, $waitingPeriodStringsWFH)) > 0) { - return AbstractController::ResultError('Đã có ticket đang chờ duyệt trong thời gian này, không thể tạo ticket mới!'); - } - - // Kiểm tra xem có sự trùng lặp giữa request mới và tickets đã được duyệt - if (count(array_intersect($periodStrings, $confirmedPeriodStrings)) > 0) { - return AbstractController::ResultError('Đã có ticket được duyệt trong thời gian này, không thể tạo ticket mới!'); - } - // Tạo thông báo về tickets waiting nếu có $waitingTicketsMessage = ''; if (!empty($dataListPeriodWaiting)) { @@ -365,7 +316,7 @@ class TicketController extends Controller $balanceCheckResult = $this->checkLeaveBalance($user, $dataListPeriod); } // dd($balanceCheckResult); - // Nếu không đủ ngày phép, trả về thông báo và không tạo ticket + // 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)) { @@ -377,89 +328,13 @@ class TicketController extends Controller return AbstractController::ResultError("Không thỏa mãn điều kiện ngày phép", $balanceCheckResult); } - } else if ($type === 'WFH') { - // Lấy thông tin tickets nghỉ phép đang ở trạng thái WAITING - $ticketsWaiting = Ticket::where('user_id', $user->id)->where('status', 'WAITING')->whereIn('type', ['WFH', '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)); - } - } - - // Lấy thông tin tickets nghỉ phép đang ở trạng thái CONFIRMED - $ticketsConfirmed = Ticket::where('user_id', $user->id) - ->where('status', 'CONFIRMED') - ->whereIn('type', ['ONLEAVE', 'WFH']) - ->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(); - - $dataListPeriodConfirmed = []; - if ($ticketsConfirmed->count() > 0) { - foreach ($ticketsConfirmed as $ticket) { - $dataListPeriodConfirmed = array_merge($dataListPeriodConfirmed, $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period)); - } - } - // dd($dataListPeriodConfirmed,$ticketsConfirmed,$start_date->toDateString(),$end_date->toDateString()); - // Chuyển đổi mảng đa chiều thành mảng chuỗi để có thể so sánh - $periodStrings = []; - $waitingPeriodStrings = []; - $confirmedPeriodStrings = []; - - foreach ($dataListPeriod as $period) { - if ($period['period'] == 'ALL') { - $periodStrings[] = $period['date'] . '_S'; - $periodStrings[] = $period['date'] . '_C'; - } else { - $periodStrings[] = $period['date'] . '_' . $period['period']; - } - } - - foreach ($dataListPeriodConfirmed as $period) { - if ($period['period'] == 'ALL') { - $confirmedPeriodStrings[] = $period['date'] . '_S'; - $confirmedPeriodStrings[] = $period['date'] . '_C'; - } else { - $confirmedPeriodStrings[] = $period['date'] . '_' . $period['period']; - } - } - - foreach ($dataListPeriodWaiting as $period) { - if ($period['period'] == 'ALL') { - $waitingPeriodStrings[] = $period['date'] . '_S'; - $waitingPeriodStrings[] = $period['date'] . '_C'; - } else { - $waitingPeriodStrings[] = $period['date'] . '_' . $period['period']; - } - } - // Kiểm tra xem có sự trùng lặp giữa request mới và tickets đang chờ duyệt - if (count(array_intersect($periodStrings, $waitingPeriodStrings)) > 0) { - return AbstractController::ResultError('Đã có ticket đang chờ duyệt trong thời gian này, không thể tạo ticket mới!'); - } - - // Kiểm tra xem có sự trùng lặp giữa request mới và tickets đã được duyệt - if (count(array_intersect($periodStrings, $confirmedPeriodStrings)) > 0) { - return AbstractController::ResultError('Đã có ticket được duyệt trong thời gian này, không thể tạo ticket mới!'); - } } // --- Kết thúc kiểm tra --- - // Nếu đủ ngày phép (hoặc loại ticket không phải ONLEAVE), tiếp tục tạo ticket + // 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, @@ -495,7 +370,7 @@ class TicketController extends Controller ); // Thêm kiểm tra null trước khi gửi mail if ($dataMasterStartPeriod && $dataMasterEndPeriod && $dataMasterType) { - Mail::to($value->email)->send(new TicketMail($data)); + Mail::to($value->email)->queue(new TicketMail($data)); } else { Log::error("Missing category data for ticket ID: {$ticket->id}. Mail not sent."); } @@ -837,11 +712,9 @@ class TicketController extends Controller public function handleTicket(Request $request) { - $rules = [ 'ticket_id' => 'required', 'action' => 'required', - // 'admin_note' => 'required' ]; // Validate the request @@ -856,21 +729,6 @@ class TicketController extends Controller if (!$ticket || $ticket->status !== "WAITING") { return response()->json(['message' => "Ticket not found", 'status' => false]); } - // Confirm - // Update updated_by and admin_note in tickets table - // Refuse - // Update updated_by and admin_note in tickets table - - // Send notification email to users - $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); $dataMasterTypeNotes = CategoryController::getListMasterByType("REASON_NOTES"); $onleave = null; @@ -882,248 +740,25 @@ class TicketController extends Controller $leaveWithoutPay = optional($dataMasterTypeNotes->where('c_code', 'LEAVE_WITHOUT_PAY')->first())->c_code; } - $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 ($onleave == null || $leaveWithoutPay == null) { return response()->json(['message' => "Data reason notes not found", 'status' => false]); } if ($action == "confirm") { - 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); - 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' => $onleave, - 'n_note' => $ticket->reason - ]); - Notes::create([ - 'n_user_id' => $ticket->user_id, - 'n_day' => $day, - 'n_month' => $month, - 'n_year' => $year, - 'n_time_type' => 'C', - 'n_reason' => $leaveWithoutPay, - 'n_note' => $ticket->reason - ]); - $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' => $onleave, - 'n_note' => $ticket->reason - ]); - $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' => $leaveWithoutPay, - 'n_note' => $ticket->reason - ]); - $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' => $onleave, // có phép - 'n_note' => $ticket->reason - ]); - } - } - - $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 - ]); - - - //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(11, 30, 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('UTC') - ], - [ - 'name' => $user->name, - 'user_id' => $user->id, - 'status' => 'check out', - 'time_string' => $end->format('Y-m-d H:i:s'), - 'created_at' => $end->setTimezone('UTC') - ] - ]); - //WFH - end tracking - } - } - - $ticket['updated_by'] = $admin->name; - $ticket['admin_note'] = $admin_note; - $ticket['status'] = 'CONFIRMED'; - $ticket->save(); - - $this->createOrUpdateRecordForCurrentMonth($month, $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)); - + $this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay); return response()->json(['message' => "confirmed", 'status' => true]); } if ($action == "refuse") { - $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)); + $this->handleRefuseTicket($ticket, $admin, $admin_note); return response()->json(['message' => "refused", 'status' => true]); } return response()->json(['message' => "failed", 'status' => false]); } - // Handle Logic same as HandleTicket, but need email to identify admin and redirect to Management page public function handleTicketEmail(Request $request) { - $rules = [ 'ticket_id' => 'required', 'action' => 'required', @@ -1145,17 +780,6 @@ class TicketController extends Controller return redirect()->to(config('app.client_url') . '/tickets-management'); } - // Send notification email to users - $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); - $dataMasterTypeNotes = CategoryController::getListMasterByType("REASON_NOTES"); $onleave = null; $leaveWithoutPay = null; @@ -1166,149 +790,171 @@ class TicketController extends Controller $leaveWithoutPay = optional($dataMasterTypeNotes->where('c_code', 'LEAVE_WITHOUT_PAY')->first())->c_code; } + if ($onleave == null || $leaveWithoutPay == null) { + return redirect()->to(config('app.client_url') . '/tickets-management'); + } + + if ($action == "confirm") { + $this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay); + 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) + { + $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 ($onleave == null || $leaveWithoutPay == null) { - // Data reason notes not found - return redirect()->to(config('app.client_url') . '/tickets-management'); - } + 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); + 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']; + }); - if ($action == "confirm") { - if ($ticket->type == "ONLEAVE") { - $dataListPeriod = $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period); - $balanceCheckResult = $this->checkLeaveBalance($user, $dataListPeriod); - // dd($balanceCheckResult,$dataListPeriod); - 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; - $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' => $onleave, - 'n_note' => $ticket->reason - ]); - Notes::create([ - 'n_user_id' => $ticket->user_id, - 'n_day' => $day, - 'n_month' => $month, - 'n_year' => $year, - 'n_time_type' => 'C', - 'n_reason' => $leaveWithoutPay, - 'n_note' => $ticket->reason - ]); - $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' => $onleave, - 'n_note' => $ticket->reason - ]); - $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' => $leaveWithoutPay, - 'n_note' => $ticket->reason - ]); - $daysWillUseWithoutPay -= $use; - } - // Nếu cả hai đều hết thì thôi, không tạo nữa + 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' => $onleave, + 'n_note' => $ticket->reason + ]); + Notes::create([ + 'n_user_id' => $ticket->user_id, + 'n_day' => $day, + 'n_month' => $month, + 'n_year' => $year, + 'n_time_type' => 'C', + 'n_reason' => $leaveWithoutPay, + 'n_note' => $ticket->reason + ]); + $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' => $onleave, + 'n_note' => $ticket->reason + ]); + $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' => $leaveWithoutPay, + 'n_note' => $ticket->reason + ]); + $daysWillUseWithoutPay -= $use; } - } - } - } 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' => $onleave, // có phép - 'n_note' => $ticket->reason - ]); - } - } - - $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 - if ($roundedExcessDays > 0) { - $leaveDaysInfo->ld_day_total += $roundedExcessDays; - $leaveDaysInfo->save(); - - Log::info("Updated leave days for User ID: {$ticket->user_id}. Added {$roundedExcessDays} days (rounded from {$excessDays})"); + // Nếu cả hai đều hết thì thôi, không tạo nữa } } } - } else if ($ticket->type == "WFH") { - $dataListPeriod = $this->getAllPeriod($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period); + } 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' => $onleave, // có phép + 'n_note' => $ticket->reason + ]); + } + } + + $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, @@ -1321,94 +967,100 @@ class TicketController extends Controller ]); - //WFH - start tracking - $type = $result['period']; - $date = Carbon::create($year, $month, $day)->setTimezone(env('TIME_ZONE')); + //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); + //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(11, 30, 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('UTC') - ], - [ - 'name' => $user->name, - 'user_id' => $user->id, - 'status' => 'check out', - 'time_string' => $end->format('Y-m-d H:i:s'), - 'created_at' => $end->setTimezone('UTC') - ] - ]); - //WFH - end tracking + if ($type == 'S') { + $end = $date->copy()->setTime(11, 30, 0); + } else if ($type == 'C') { + $start = $date->copy()->setTime(11, 30, 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('UTC') + ], + [ + 'name' => $user->name, + 'user_id' => $user->id, + 'status' => 'check out', + 'time_string' => $end->format('Y-m-d H:i:s'), + 'created_at' => $end->setTimezone('UTC') + ] + ]); + //WFH - end tracking } - - $ticket['updated_by'] = $admin->name; - $ticket['admin_note'] = $admin_note; - $ticket['status'] = 'CONFIRMED'; - $ticket->save(); - - $this->createOrUpdateRecordForCurrentMonth($month, $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)); - - // Confirm Success - return redirect()->to(config('app.client_url') . '/tickets-management'); } - if ($action == "refuse") { - $ticket['updated_by'] = $admin->name; - $ticket['admin_note'] = $admin_note; - $ticket['status'] = 'REFUSED'; - $ticket->save(); + $ticket['updated_by'] = $admin->name; + $ticket['admin_note'] = $admin_note; + $ticket['status'] = 'CONFIRMED'; + $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)); + $this->createOrUpdateRecordForCurrentMonth($month, $year); - // Refuse Success - return redirect()->to(config('app.client_url') . '/tickets-management'); - } - - // Failed - return redirect()->to(config('app.client_url') . '/tickets-management'); + // 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)