From 7217a83e33ed24e7dd99e9e16404893b2789f189 Mon Sep 17 00:00:00 2001 From: dbdbd9 Date: Wed, 11 Jun 2025 16:44:11 +0700 Subject: [PATCH] add confirm/refuse in ticket mail --- .../app/Http/Controllers/TicketController.php | 293 ++++++++++++++++++ BACKEND/Modules/Admin/routes/api.php | 1 + BACKEND/config/app.php | 2 + ...ay_to_ld_day_total_in_leave_days_table.php | 4 +- .../email/notification_tickets.blade.php | 118 +++++-- 5 files changed, 397 insertions(+), 21 deletions(-) diff --git a/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php b/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php index 8ea27d8..3cc2fbb 100644 --- a/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php +++ b/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php @@ -364,8 +364,10 @@ class TicketController extends Controller $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, @@ -984,6 +986,297 @@ class TicketController extends Controller 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', + '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') . '/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; + + 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; + } + + $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 ($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; + + 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 + 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})"); + } + } + } + } 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)); + + // 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(); + + $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)); + + // Refuse Success + return redirect()->to(config('app.client_url') . '/tickets-management'); + } + + // Failed + return redirect()->to(config('app.client_url') . '/tickets-management'); + } + private function getAllPeriodNew($startDate, $startPeriod, $endDate, $endPeriod) { // Đảm bảo $startDate và $endDate là đối tượng Carbon diff --git a/BACKEND/Modules/Admin/routes/api.php b/BACKEND/Modules/Admin/routes/api.php index 8f9ee2c..ae1b8db 100755 --- a/BACKEND/Modules/Admin/routes/api.php +++ b/BACKEND/Modules/Admin/routes/api.php @@ -44,6 +44,7 @@ Route::middleware('api') Route::post('login', [AdminController::class, 'login']); Route::post('reset-password', [AdminController::class, 'resetPassword']); Route::get('forgot-password', [AdminController::class, 'forgotPassword']); + Route::get('/email-handle-ticket', [TicketController::class, 'handleTicketEmail'])->name('email.ticket.handle'); }); // NOTE after login diff --git a/BACKEND/config/app.php b/BACKEND/config/app.php index 6e6a756..8b3161b 100755 --- a/BACKEND/config/app.php +++ b/BACKEND/config/app.php @@ -59,6 +59,8 @@ return [ 'asset_url' => env('ASSET_URL'), + 'client_url' => env('ADMIN_URL', 'http://localhost'), + /* |-------------------------------------------------------------------------- | Application Timezone diff --git a/BACKEND/database/migrations/2025_03_13_070714_rename_ld_day_to_ld_day_total_in_leave_days_table.php b/BACKEND/database/migrations/2025_03_13_070714_rename_ld_day_to_ld_day_total_in_leave_days_table.php index 51b4754..e280937 100644 --- a/BACKEND/database/migrations/2025_03_13_070714_rename_ld_day_to_ld_day_total_in_leave_days_table.php +++ b/BACKEND/database/migrations/2025_03_13_070714_rename_ld_day_to_ld_day_total_in_leave_days_table.php @@ -9,14 +9,14 @@ class RenameLdDayToLdDayTotalInLeaveDaysTable extends Migration public function up() { Schema::table('leave_days', function (Blueprint $table) { - $table->renameColumn('ld_day_total', 'ld_day_total'); + $table->renameColumn('ld_day', 'ld_day_total'); }); } public function down() { Schema::table('leave_days', function (Blueprint $table) { - $table->renameColumn('ld_day_total', 'ld_day_total'); + $table->renameColumn('ld_day_total', 'ld_day'); }); } } diff --git a/BACKEND/resources/views/email/notification_tickets.blade.php b/BACKEND/resources/views/email/notification_tickets.blade.php index a013eed..08a39e7 100644 --- a/BACKEND/resources/views/email/notification_tickets.blade.php +++ b/BACKEND/resources/views/email/notification_tickets.blade.php @@ -98,29 +98,109 @@ + -

- - Check now -

+

+ + Check now +

+ +

+ Or you can quick + Confirm or + Refuse here: +

+ +
+ + Confirm + + + Refuse +
- -- 2.39.2