diff --git a/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php b/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php index 1f7ee90..cac2408 100644 --- a/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php +++ b/BACKEND/Modules/Admin/app/Http/Controllers/TicketController.php @@ -553,11 +553,20 @@ class TicketController extends Controller // 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 ($onleaveDaysInMonth >= $maxDaysPerMonth) { + if ($willUsedDaysInMonth > $maxDaysPerMonth) { $hasInsufficientDays = true; $month_data_status = 'exceed_max_days'; - $onleave_days_will_use = $maxDaysPerMonth - $onleaveDaysInMonth; - $nopay_days_will_use = $monthData['days_requested'] - $maxDaysPerMonth + $onleaveDaysInMonth; + + // 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, @@ -1409,4 +1418,147 @@ class TicketController extends Controller 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++; + $ld_note = $ld_note . "Cộng phép tháng " . $i . ".\n"; + // $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(); + } + } } diff --git a/BACKEND/app/Jobs/AddMonthlyLeaveDays.php b/BACKEND/app/Jobs/AddMonthlyLeaveDays.php index 38fd483..ee3bd2b 100644 --- a/BACKEND/app/Jobs/AddMonthlyLeaveDays.php +++ b/BACKEND/app/Jobs/AddMonthlyLeaveDays.php @@ -67,7 +67,7 @@ class AddMonthlyLeaveDays implements ShouldQueue $leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH; // Xử lý ghi chú - $newNote = "Cập nhật ngày phép đến tháng " . $this->month; + $newNote = "Cộng phép tháng " . $leaveDay->ld_day_total . ".\n"; if (!empty($leaveDay->ld_note)) { // Nếu đã có ghi chú, thêm ghi chú mới vào và xuống dòng $leaveDay->ld_note = $leaveDay->ld_note . "\n" . $newNote; @@ -86,7 +86,7 @@ class AddMonthlyLeaveDays implements ShouldQueue $leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH; // Xử lý ghi chú - $newNote = "Cập nhật ngày phép đến tháng " . $this->month; + $newNote = "Cộng phép tháng " . $leaveDay->ld_day_total . ".\n"; if (!empty($leaveDay->ld_note)) { // Nếu đã có ghi chú, thêm ghi chú mới vào và xuống dòng $leaveDay->ld_note = $leaveDay->ld_note . "\n" . $newNote; diff --git a/BACKEND/app/Jobs/DeductLeaveDays.php b/BACKEND/app/Jobs/DeductLeaveDays.php index e7e61b7..3dd165a 100644 --- a/BACKEND/app/Jobs/DeductLeaveDays.php +++ b/BACKEND/app/Jobs/DeductLeaveDays.php @@ -52,12 +52,16 @@ class DeductLeaveDays implements ShouldQueue ->where('n_reason', 'ONLEAVE') ->sum('categories.c_value'); - if($usedOnleaveDaysTotal) { - $existingData->ld_additional_day = $existingData->ld_additional_day >= $usedOnleaveDaysTotal ? $usedOnleaveDaysTotal : $existingData->ld_additional_day; + if ($usedOnleaveDaysTotal) { + if ($existingData->ld_additional_day > $usedOnleaveDaysTotal) { + $ld_note = "Trừ " . $existingData->ld_additional_day - $usedOnleaveDaysTotal . " ngày phép tồn năm trước. \n"; + $existingData->ld_note = $existingData->ld_note . "\n" . $ld_note; + $existingData->ld_additional_day = $usedOnleaveDaysTotal; + } } else { $existingData->ld_additional_day = 0; } - + $existingData->save(); } } diff --git a/BACKEND/app/Jobs/InitializeLeaveDays.php b/BACKEND/app/Jobs/InitializeLeaveDays.php index 6ba8114..424745c 100644 --- a/BACKEND/app/Jobs/InitializeLeaveDays.php +++ b/BACKEND/app/Jobs/InitializeLeaveDays.php @@ -77,7 +77,10 @@ class InitializeLeaveDays implements ShouldQueue $ld_additional_day = 0; } } - $ld_note = 'Cộng dồn ngày phép năm cũ'; + + if ($ld_additional_day > 0) { + $ld_note = "Cộng " . $ld_additional_day . " ngày phép tồn năm trước. \n"; + } } // Tạo dữ liệu cho năm hiện tại diff --git a/BACKEND/tests/UpdateOldNoteData.php b/BACKEND/tests/UpdateOldNoteData.php new file mode 100644 index 0000000..3c0a458 --- /dev/null +++ b/BACKEND/tests/UpdateOldNoteData.php @@ -0,0 +1,13 @@ +make(Illuminate\Contracts\Console\Kernel::class); +$kernel->bootstrap(); + +// Cập nhật lại data cho tới tháng hiện tại +$currentMonth = Carbon::now()->month; +$tmpClass = $app->make('Modules\Admin\app\Http\Controllers\TicketController'); +$tmpClass->updateOldData($currentMonth, 2025); // Params: month, year diff --git a/FRONTEND/src/pages/LeaveManagement/LeaveManagement.tsx b/FRONTEND/src/pages/LeaveManagement/LeaveManagement.tsx index 640b95b..63aabe1 100644 --- a/FRONTEND/src/pages/LeaveManagement/LeaveManagement.tsx +++ b/FRONTEND/src/pages/LeaveManagement/LeaveManagement.tsx @@ -1,19 +1,18 @@ -import { - getLeaveManagement, - updateNoteLeave, - exportLeaveManagement, - // getListMaster, -} from '@/api/Admin' -import { update } from '@/rtk/helpers/CRUD' -import { get, exportFile } from '@/rtk/helpers/apiService' +import { useEffect, useState } from 'react' +import moment from 'moment' + import { Avatar, + Badge, Box, Button, Drawer, + Flex, + Group, HoverCard, Menu, Select, + Stack, Table, Text, Textarea, @@ -22,13 +21,17 @@ import { } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { notifications } from '@mantine/notifications' -import moment from 'moment' -import { useEffect, useState } from 'react' - import { IconEdit, IconFileExcel } from '@tabler/icons-react' - import classes from './LeaveManagement.module.css' +import { + getLeaveManagement, + updateNoteLeave, + exportLeaveManagement, +} from '@/api/Admin' +import { update } from '@/rtk/helpers/CRUD' +import { get, exportFile } from '@/rtk/helpers/apiService' + interface User { id: number name: string @@ -265,8 +268,6 @@ const LeaveManagement = () => { }) } - // console.log(customAddNotes, 'customAddNotes') - const getDetailLeaveDay = (monthlyLeaveDays: MonthlyLeaveDays[]) => { type MonthlyLeaveDaysAcc = { [key: string]: { n_user_id: number; month: number; leave_days: number } @@ -290,29 +291,87 @@ const LeaveManagement = () => { } const showAllOff = (monthlyLeaveDays: MonthlyLeaveDays[]) => { - let lastmonth = 0 - return monthlyLeaveDays.map((itemDay, indexDay) => { - const isNewMonth = lastmonth !== itemDay.month - if (isNewMonth) { - lastmonth = itemDay.month + return monthInYear.map((d, i) => { + let totalOnLeaveMonth = 0 + let totalLeaveWithoutPayMonth = 0 + + monthlyLeaveDays + .filter((item) => item.month === d.value) + .map((item) => { + if (item.reason_code === 'ONLEAVE') { + totalOnLeaveMonth += Number(item.leave_days) + } else { + totalLeaveWithoutPayMonth += Number(item.leave_days) + } + }) + + if (totalOnLeaveMonth === 0 && totalLeaveWithoutPayMonth === 0) { + return '' } + return ( -
Month {lastmonth}
} -- - {itemDay.reason_name} ({itemDay.time_type_name}) {itemDay.day}/ - {itemDay.month} -
-