truong-leave-day #116
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -44,13 +44,14 @@ class LeaveManagementController extends Controller
 | 
			
		|||
                DB::raw('notes.n_month as month'),
 | 
			
		||||
                DB::raw('categories.c_value as leave_days'),
 | 
			
		||||
                DB::raw('notes.n_day as day'),
 | 
			
		||||
                DB::raw('notes.n_reason as reason_code'),
 | 
			
		||||
                'reason.c_name as reason_name',
 | 
			
		||||
                'categories.c_name as time_type_name',
 | 
			
		||||
                // DB::raw('SUM(categories.c_value) as leave_days')
 | 
			
		||||
            )
 | 
			
		||||
            // ->where('notes.n_user_id', "1")
 | 
			
		||||
            ->where('notes.n_year', $year)
 | 
			
		||||
            ->where('notes.n_reason', 'ONLEAVE')
 | 
			
		||||
            ->whereIn('notes.n_reason', ['ONLEAVE', 'LEAVE_WITHOUT_PAY'])
 | 
			
		||||
            // ->groupBy("notes.n_user_id")
 | 
			
		||||
            ->orderBy('notes.n_month')
 | 
			
		||||
            ->orderBy('notes.n_day')
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +60,7 @@ class LeaveManagementController extends Controller
 | 
			
		|||
                return [
 | 
			
		||||
                    "day" => $item->day,
 | 
			
		||||
                    "n_user_id" => $item->n_user_id,
 | 
			
		||||
                    // "time_type" => $item->time_type,
 | 
			
		||||
                    "reason_code" => $item->reason_code,
 | 
			
		||||
                    "reason_name" => $item->reason_name,
 | 
			
		||||
                    "time_type_name" => $item->time_type_name,
 | 
			
		||||
                    "month" => $item->month,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,10 @@ 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 Illuminate\Http\JsonResponse;
 | 
			
		||||
use App\Models\Admin as UserModel;
 | 
			
		||||
 | 
			
		||||
class TicketController extends Controller
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -204,29 +208,47 @@ class TicketController extends Controller
 | 
			
		|||
        $request->validate($rules);
 | 
			
		||||
        // return $request;
 | 
			
		||||
 | 
			
		||||
        //Get data from request
 | 
			
		||||
        $startDate = $request->input('start_date'); //Start day
 | 
			
		||||
        $startPeriod = $request->input('start_period'); //The session begins
 | 
			
		||||
        $endDate = $request->input('end_date'); //End date
 | 
			
		||||
        $endPeriod = $request->input('end_period'); //Session ends
 | 
			
		||||
        // Get data from request
 | 
			
		||||
        $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(); // user create ticket
 | 
			
		||||
 | 
			
		||||
        // return $user;
 | 
			
		||||
        $start_date = Carbon::create($startDate)->setTimezone(env('TIME_ZONE'));
 | 
			
		||||
        $end_date = Carbon::create($endDate)->setTimezone(env('TIME_ZONE'));
 | 
			
		||||
        $currentYear = $start_date->year;
 | 
			
		||||
 | 
			
		||||
        $dataMasterStartPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $startPeriod);
 | 
			
		||||
        $dataMasterEndPeriod = CategoryController::getListMasterByCodeAndType("TIME_TYPE", $endPeriod);
 | 
			
		||||
        $dataMasterType = CategoryController::getListMasterByCodeAndType("REASON", $type);
 | 
			
		||||
        // --- Chỉ kiểm tra ngày phép khi loại là ONLEAVE ---
 | 
			
		||||
        if ($type === 'ONLEAVE' && !$isAccept) {
 | 
			
		||||
            // Get mảng ngày nghỉ và tính tổng số ngày yêu cầu
 | 
			
		||||
            $dataListPeriod = $this->getAllPeriodNew($start_date, $startPeriod, $end_date, $endPeriod);
 | 
			
		||||
            if (empty($dataListPeriod)) {
 | 
			
		||||
                return response()->json(['message' => 'Không thể tính toán khoảng thời gian nghỉ hợp lệ.', 'status' => false]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        $formattedStartDate = Carbon::createFromFormat('Y-m-d', $startDate)->format('d/m/Y');
 | 
			
		||||
        $formattedEndDate = Carbon::createFromFormat('Y-m-d', $endDate)->format('d/m/Y');
 | 
			
		||||
            // Kiểm tra số dư ngày phép
 | 
			
		||||
            $balanceCheckResult = $this->checkLeaveBalance($user, $currentYear, $dataListPeriod);
 | 
			
		||||
            dd($balanceCheckResult);
 | 
			
		||||
            // Nếu không đủ ngày phép, trả về thông báo và không tạo ticket
 | 
			
		||||
            if (!$balanceCheckResult['success']) {
 | 
			
		||||
                return response()->json([
 | 
			
		||||
                    'message' => $balanceCheckResult['message'],
 | 
			
		||||
                    'status' => false
 | 
			
		||||
                ]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        dd("đã qua kiểm tra");
 | 
			
		||||
        // --- Kết thúc kiểm tra ---
 | 
			
		||||
 | 
			
		||||
        $user = auth('admins')->user();
 | 
			
		||||
        // Nếu đủ ngày phép (hoặc loại ticket không phải ONLEAVE), tiếp tục tạo ticket
 | 
			
		||||
        $ticket = Ticket::create([
 | 
			
		||||
            'start_date' => Carbon::create($startDate)->setTimezone(env('TIME_ZONE')),
 | 
			
		||||
            'start_date' => $start_date->toDateString(),
 | 
			
		||||
            'start_period' => $startPeriod,
 | 
			
		||||
            'end_date' => Carbon::create($endDate)->setTimezone(env('TIME_ZONE')),
 | 
			
		||||
            'end_date' => $end_date->toDateString(),
 | 
			
		||||
            'end_period' => $endPeriod,
 | 
			
		||||
            'type' => $type,
 | 
			
		||||
            'status' => 'WAITING',
 | 
			
		||||
| 
						 | 
				
			
			@ -235,24 +257,237 @@ class TicketController extends Controller
 | 
			
		|||
        ]);
 | 
			
		||||
 | 
			
		||||
        // 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(
 | 
			
		||||
                "email_template" => "email.notification_tickets",
 | 
			
		||||
                "email" => $user->email,
 | 
			
		||||
                "name" => $user->name,
 | 
			
		||||
                "date" => $dataMasterStartPeriod->c_name . " (" . $formattedStartDate . ") - " . $dataMasterEndPeriod->c_name . " (" . $formattedEndDate . ")",
 | 
			
		||||
                "type" => $dataMasterType->c_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)->send(new TicketMail($data));
 | 
			
		||||
            } else {
 | 
			
		||||
                Log::error("Missing category data for ticket ID: {$ticket->id}. Mail not sent.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response()->json(['data' => $ticket, 'status' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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 int $year Năm kiểm tra
 | 
			
		||||
     * @param float $daysRequested Số ngày yêu cầu nghỉ
 | 
			
		||||
     * @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, int $year, array $dataListPeriod = null): array
 | 
			
		||||
    {
 | 
			
		||||
        // Tính tổng số ngày yêu cầu
 | 
			
		||||
        $daysRequested = $this->calculateTotalLeaveDays($dataListPeriod);
 | 
			
		||||
 | 
			
		||||
        // 1. Tính tổng ngày phép được cấp
 | 
			
		||||
        $totalAllocated = $this->getTotalAllocatedDays($user, $year);
 | 
			
		||||
 | 
			
		||||
        // 2. Tính số ngày đã nghỉ trong năm
 | 
			
		||||
        $usedDays = $this->getUsedLeaveDays($user, $year);
 | 
			
		||||
 | 
			
		||||
        // 3. Tính số ngày còn lại
 | 
			
		||||
        $remainingDays = $totalAllocated - $usedDays;
 | 
			
		||||
 | 
			
		||||
        // 4. Kiểm tra giới hạn nghỉ phép theo tháng
 | 
			
		||||
        $monthsInfo = [];
 | 
			
		||||
        if (!empty($dataListPeriod)) {
 | 
			
		||||
            $monthlyCheckResult = $this->checkMonthlyLeaveLimit($user, $dataListPeriod);
 | 
			
		||||
            if (!$monthlyCheckResult['success']) {
 | 
			
		||||
                return $monthlyCheckResult;
 | 
			
		||||
            }
 | 
			
		||||
            $monthsInfo = $monthlyCheckResult['months_info'];
 | 
			
		||||
            if (!empty($monthsInfo)) {
 | 
			
		||||
                //Danh sách ngày nghỉ trong tháng dựa trên list ngày xin nghỉ
 | 
			
		||||
                
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 5. Kiểm tra đủ ngày phép không
 | 
			
		||||
        if ($remainingDays < $daysRequested) {
 | 
			
		||||
            return $this->insufficientLeaveDaysResponse($user, $remainingDays, $daysRequested);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 6. Kiểm tra giới hạn ngày liên tục
 | 
			
		||||
        if ($daysRequested > 3) {
 | 
			
		||||
            return $this->exceedMaxConsecutiveDaysResponse($user, $daysRequested);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Đủ điều kiện
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
            'message' => null,
 | 
			
		||||
            'remaining_days' => $remainingDays,
 | 
			
		||||
            'months_info' => $monthsInfo
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkMonthlyLeaveLimit($user, array $dataListPeriod): array
 | 
			
		||||
    {
 | 
			
		||||
        $requestMonths = $this->groupLeaveRequestsByMonth($dataListPeriod);
 | 
			
		||||
        $monthsInfo = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($requestMonths as $monthKey => $monthData) {
 | 
			
		||||
            $usedDaysInMonth = $this->getUsedLeaveDaysInMonth($user, $monthData['year'], $monthData['month']);
 | 
			
		||||
            dd($usedDaysInMonth);
 | 
			
		||||
            $requestMonths[$monthKey]['days_used'] = $usedDaysInMonth;
 | 
			
		||||
 | 
			
		||||
            // Kiểm tra giới hạn ngày nghỉ trong tháng
 | 
			
		||||
            $maxDaysPerMonth = 3; // Có thể điều chỉnh theo quy định công ty
 | 
			
		||||
            $totalDaysInMonth = $usedDaysInMonth + $monthData['days_requested'];
 | 
			
		||||
 | 
			
		||||
            if ($totalDaysInMonth > $maxDaysPerMonth) {
 | 
			
		||||
                return $this->exceedMonthlyLimitResponse($monthData, $usedDaysInMonth, $maxDaysPerMonth);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $monthsInfo[] = [
 | 
			
		||||
                'year' => $monthData['year'],
 | 
			
		||||
                'month' => $monthData['month'],
 | 
			
		||||
                'days_used' => $usedDaysInMonth,
 | 
			
		||||
                'days_requested' => $monthData['days_requested'],
 | 
			
		||||
                'total_days' => $totalDaysInMonth
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
            'months_info' => $monthsInfo
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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): 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 exceedMonthlyLimitResponse(array $monthData, float $usedDaysInMonth, int $maxDaysPerMonth): array
 | 
			
		||||
    {
 | 
			
		||||
        $exceedDays = ($usedDaysInMonth + $monthData['days_requested']) - $maxDaysPerMonth;
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => false,
 | 
			
		||||
            'message' => "Trong tháng {$monthData['month']}/{$monthData['year']}, bạn đã nghỉ {$usedDaysInMonth} ngày và xin nghỉ thêm {$monthData['days_requested']} ngày, vượt quá giới hạn {$maxDaysPerMonth} ngày/tháng. Vui lòng điều chỉnh lại.",
 | 
			
		||||
            'warning_type' => 'exceed_monthly_limit',
 | 
			
		||||
            'month_data' => [
 | 
			
		||||
                'year' => $monthData['year'],
 | 
			
		||||
                'month' => $monthData['month'],
 | 
			
		||||
                'days_used' => $usedDaysInMonth,
 | 
			
		||||
                'days_requested' => $monthData['days_requested'],
 | 
			
		||||
                'max_days' => $maxDaysPerMonth,
 | 
			
		||||
                'exceed_days' => $exceedDays
 | 
			
		||||
            ]
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function insufficientLeaveDaysResponse($user, float $remainingDays, float $daysRequested): array
 | 
			
		||||
    {
 | 
			
		||||
        $daysNotEnough = $daysRequested - $remainingDays;
 | 
			
		||||
        Log::warning("Insufficient leave balance for user ID: {$user->id}. Remaining: {$remainingDays}, Requested: {$daysRequested}");
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => false,
 | 
			
		||||
            'message' => "Bạn không đủ ngày phép. Số ngày phép còn lại: {$remainingDays}, Số ngày yêu cầu: {$daysRequested}. Bạn có chấp nhận nộp: {$daysNotEnough} ngày không phép không?"
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function exceedMaxConsecutiveDaysResponse($user, float $daysRequested): array
 | 
			
		||||
    {
 | 
			
		||||
        $noLeavePermissionDays = $daysRequested - 3;
 | 
			
		||||
        $message = "Bạn đã yêu cầu {$daysRequested} ngày nghỉ. Theo quy định, chỉ 3 ngày đầu là nghỉ có phép, {$noLeavePermissionDays} ngày còn lại sẽ là nghỉ không phép. Bạn có chấp nhận tiếp tục không?";
 | 
			
		||||
        Log::info("User ID: {$user->id} requested {$daysRequested} days which exceeds the 3-day limit. {$noLeavePermissionDays} days will be marked as leave without permission.");
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
            'message' => $message,
 | 
			
		||||
            'require_acceptance' => true,
 | 
			
		||||
            'warning_type' => 'exceed_max_days',
 | 
			
		||||
            'max_leave_days' => 3,
 | 
			
		||||
            'days_with_permission' => 3,
 | 
			
		||||
            'days_without_permission' => $noLeavePermissionDays
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function deleteTicket(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
        $rules = [
 | 
			
		||||
| 
						 | 
				
			
			@ -424,6 +659,70 @@ class TicketController extends Controller
 | 
			
		|||
        return response()->json(['message' => "failed", 'status' => false]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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');
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($period as $date) {
 | 
			
		||||
            // Bỏ qua Thứ 7 (6) và Chủ Nhật (0)
 | 
			
		||||
            if ($date->dayOfWeek === Carbon::SATURDAY || $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
 | 
			
		||||
| 
						 | 
				
			
			@ -488,4 +787,33 @@ class TicketController extends Controller
 | 
			
		|||
        //Returns results
 | 
			
		||||
        return $results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import {
 | 
			
		|||
  getLeaveManagement,
 | 
			
		||||
  updateNoteLeave,
 | 
			
		||||
  exportLeaveManagement,
 | 
			
		||||
  getListMaster,
 | 
			
		||||
} from '@/api/Admin'
 | 
			
		||||
import { update } from '@/rtk/helpers/CRUD'
 | 
			
		||||
import { get, exportFile } from '@/rtk/helpers/apiService'
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +58,7 @@ interface MonthlyLeaveDays {
 | 
			
		|||
  leave_days: number
 | 
			
		||||
  month: number
 | 
			
		||||
  n_user_id: number
 | 
			
		||||
  reason_code: string
 | 
			
		||||
  reason_name: string
 | 
			
		||||
  time_type_name: string
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +69,18 @@ interface UserData {
 | 
			
		|||
  monthlyLeaveDays: MonthlyLeaveDays[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface DataReason {
 | 
			
		||||
  id: number
 | 
			
		||||
  c_code: string
 | 
			
		||||
  c_name: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface DataTimeType {
 | 
			
		||||
  id: number
 | 
			
		||||
  c_code: string
 | 
			
		||||
  c_name: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const LeaveManagement = () => {
 | 
			
		||||
  const [opened1, { open: open1, close: close1 }] = useDisclosure(false)
 | 
			
		||||
  const [disableBtn, setDisableBtn] = useState(false)
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +111,42 @@ const LeaveManagement = () => {
 | 
			
		|||
  const [date, setDate] = useState({
 | 
			
		||||
    year: new Date().getFullYear().toString(),
 | 
			
		||||
  })
 | 
			
		||||
  const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
 | 
			
		||||
  const [dataReason, setDataReason] = useState<DataReason[]>([])
 | 
			
		||||
 | 
			
		||||
  const getListMasterByType = async (type: string) => {
 | 
			
		||||
    try {
 | 
			
		||||
      const params = {
 | 
			
		||||
        type: type,
 | 
			
		||||
      }
 | 
			
		||||
      const res = await get(getListMaster, params)
 | 
			
		||||
      if (res.status) {
 | 
			
		||||
        return res.data
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error: any) {
 | 
			
		||||
      notifications.show({
 | 
			
		||||
        title: 'Error',
 | 
			
		||||
        message: error.message ?? error,
 | 
			
		||||
        color: 'red',
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    return []
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const fetchData = async () => {
 | 
			
		||||
      const resultTimeType = await getListMasterByType('TIME_TYPE')
 | 
			
		||||
      setDataTimeType(
 | 
			
		||||
        resultTimeType.filter((item: DataTimeType) => item.c_code !== 'ALL'),
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      const resultReason = await getListMasterByType('REASON')
 | 
			
		||||
      setDataReason(resultReason)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fetchData()
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const getLeaveList = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      const res = await get(getLeaveManagement, {
 | 
			
		||||
| 
						 | 
				
			
			@ -505,8 +555,16 @@ const LeaveManagement = () => {
 | 
			
		|||
            {data.map((user, index) => {
 | 
			
		||||
              let totalDayOff = 0
 | 
			
		||||
              let totalDayLeave =
 | 
			
		||||
                user.leaveDay.ld_day_total + user.leaveDay.ld_additional_day
 | 
			
		||||
                user.leaveDay.ld_day_total +
 | 
			
		||||
                user.leaveDay.ld_additional_day +
 | 
			
		||||
                user.leaveDay.ld_special_leave_day
 | 
			
		||||
              let ld_day_total = user.leaveDay.ld_day_total
 | 
			
		||||
              let ld_additional_day = user.leaveDay.ld_additional_day
 | 
			
		||||
              let ld_special_leave_day = user.leaveDay.ld_special_leave_day
 | 
			
		||||
              let ld_note = user.leaveDay.ld_note
 | 
			
		||||
              let totalOnLeave = 0
 | 
			
		||||
              let totalLeaveWithoutPay = 0
 | 
			
		||||
 | 
			
		||||
              return (
 | 
			
		||||
                <Table.Tr key={user.user.id} className={classes.tableTr}>
 | 
			
		||||
                  <Table.Td ta={'center'}>{index + 1}</Table.Td>
 | 
			
		||||
| 
						 | 
				
			
			@ -539,6 +597,18 @@ const LeaveManagement = () => {
 | 
			
		|||
                    const monthData = leaveDataByMonth[d.value]
 | 
			
		||||
                    let total = monthData ? monthData.leave_days : 0
 | 
			
		||||
                    totalDayOff = totalDayOff + total
 | 
			
		||||
 | 
			
		||||
                    user.monthlyLeaveDays
 | 
			
		||||
                      .filter((item) => item.month === d.value)
 | 
			
		||||
                      .map((item) => {
 | 
			
		||||
                        if (item.reason_code === 'ONLEAVE') {
 | 
			
		||||
                          totalOnLeave = totalOnLeave + Number(item.leave_days)
 | 
			
		||||
                        } else {
 | 
			
		||||
                          totalLeaveWithoutPay =
 | 
			
		||||
                            totalLeaveWithoutPay + Number(item.leave_days)
 | 
			
		||||
                        }
 | 
			
		||||
                      })
 | 
			
		||||
 | 
			
		||||
                    return (
 | 
			
		||||
                      <Table.Td
 | 
			
		||||
                        bg={total > 0 ? '#ffb5b5' : ''}
 | 
			
		||||
| 
						 | 
				
			
			@ -565,35 +635,70 @@ const LeaveManagement = () => {
 | 
			
		|||
                    )
 | 
			
		||||
                  })}
 | 
			
		||||
 | 
			
		||||
                  {/* Total */}
 | 
			
		||||
                  <Table.Td
 | 
			
		||||
                    ta={'center'}
 | 
			
		||||
                    bg={totalDayLeave > 0 ? '#92e6f2' : ''}
 | 
			
		||||
                    // bg={totalDayLeave > 0 ? '#92e6f2' : ''}
 | 
			
		||||
                  >
 | 
			
		||||
                    {totalDayLeave}
 | 
			
		||||
                    <p
 | 
			
		||||
                      style={{
 | 
			
		||||
                        backgroundColor: '#c3ffc3',
 | 
			
		||||
                        display: ld_day_total > 0 ? 'block' : 'none',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {ld_day_total}
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <p
 | 
			
		||||
                      style={{
 | 
			
		||||
                        backgroundColor: '#92e6f2',
 | 
			
		||||
                        display: ld_additional_day > 0 ? 'block' : 'none',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {ld_additional_day}
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <p
 | 
			
		||||
                      style={{
 | 
			
		||||
                        backgroundColor: '#b5cafb',
 | 
			
		||||
                        display: ld_special_leave_day > 0 ? 'block' : 'none',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {ld_special_leave_day}
 | 
			
		||||
                    </p>
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
                  <Table.Td ta={'center'} bg={totalDayOff > 0 ? '#ffb5b5' : ''}>
 | 
			
		||||
 | 
			
		||||
                  {/* Off */}
 | 
			
		||||
                  <Table.Td ta={'center'}>
 | 
			
		||||
                    {totalDayOff > 0 ? (
 | 
			
		||||
                      <Tooltip
 | 
			
		||||
                        multiline
 | 
			
		||||
                        label={showAllOff(user.monthlyLeaveDays)}
 | 
			
		||||
                      >
 | 
			
		||||
                        <p> {totalDayOff}</p>
 | 
			
		||||
                        <div>
 | 
			
		||||
                          <p style={{ backgroundColor: '#c3ffc3' }}>
 | 
			
		||||
                            {totalOnLeave}
 | 
			
		||||
                          </p>
 | 
			
		||||
                          <p style={{ backgroundColor: '#ffb5b5' }}>
 | 
			
		||||
                            {totalLeaveWithoutPay}
 | 
			
		||||
                          </p>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      </Tooltip>
 | 
			
		||||
                    ) : (
 | 
			
		||||
                      <></>
 | 
			
		||||
                    )}
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
 | 
			
		||||
                  {/* Remaining */}
 | 
			
		||||
                  <Table.Td
 | 
			
		||||
                    ta={'center'}
 | 
			
		||||
                    bg={
 | 
			
		||||
                      totalDayLeave - totalDayOff == 0
 | 
			
		||||
                      totalDayLeave - totalOnLeave == 0
 | 
			
		||||
                        ? ''
 | 
			
		||||
                        : totalDayLeave - totalDayOff > 0
 | 
			
		||||
                        : totalDayLeave - totalOnLeave > 0
 | 
			
		||||
                        ? '#c3ffc3'
 | 
			
		||||
                        : '#ffb5b5'
 | 
			
		||||
                    }
 | 
			
		||||
                  >
 | 
			
		||||
                    {totalDayLeave - totalDayOff}
 | 
			
		||||
                    {totalDayLeave - totalOnLeave}
 | 
			
		||||
                  </Table.Td>
 | 
			
		||||
                  <Table.Td>
 | 
			
		||||
                    <Box
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue