update handle calculate onleave ticket in next months

This commit is contained in:
dbdbd9 2025-06-30 10:57:02 +07:00
parent 543b1af58c
commit 034b9eee2c
11 changed files with 406 additions and 148 deletions

View File

@ -51,7 +51,7 @@ class LeaveManagementController extends Controller
)
// ->where('notes.n_user_id', "1")
->where('notes.n_year', $year)
->whereIn('notes.n_reason', ['ONLEAVE', 'LEAVE_WITHOUT_PAY'])
->whereIn('notes.n_reason', ['ONLEAVE', 'LEAVE_WITHOUT_PAY', 'TEMPORARY_ONLEAVE'])
// ->groupBy("notes.n_user_id")
->orderBy('notes.n_month')
->orderBy('notes.n_day')
@ -155,7 +155,7 @@ class LeaveManagementController extends Controller
}
// Lọc chỉ lấy user có permission bao gồm staff
$staffData = $leaveDays->filter(function($user) {
$staffData = $leaveDays->filter(function ($user) {
return isset($user['user']['permission']) && strpos($user['user']['permission'], 'staff') !== false;
});

View File

@ -402,6 +402,10 @@ class TicketController extends Controller
$ticket->updated_by = $admin->name;
$ticket->admin_note = $request->admin_note;
// Clear Timekeeping cache
$this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
$this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
$ticket->save();
return AbstractController::ResultSuccess($ticket, "Ticket updated successfully!");
@ -717,6 +721,7 @@ class TicketController extends Controller
->first();
$totalAllocated = 0;
// Xử lý gửi ticket sau tháng hiện tại
if ($leaveDaysInfo && $user->is_permanent) {
$currentMonth = Carbon::now()->month;
$totalAllocated = $leaveDaysInfo->ld_day_total;
@ -837,19 +842,21 @@ class TicketController extends Controller
$dataMasterTypeNotes = CategoryController::getListMasterByType("REASON_NOTES");
$onleave = null;
$leaveWithoutPay = null;
$temporaryOnleave = null;
if ($dataMasterTypeNotes) {
// get nghỉ phép, nghỉ không phép
$onleave = optional($dataMasterTypeNotes->where('c_code', 'ONLEAVE')->first())->c_code;
$leaveWithoutPay = optional($dataMasterTypeNotes->where('c_code', 'LEAVE_WITHOUT_PAY')->first())->c_code;
$temporaryOnleave = optional($dataMasterTypeNotes->where('c_code', 'TEMPORARY_ONLEAVE')->first())->c_code;
}
if ($onleave == null || $leaveWithoutPay == null) {
if ($onleave == null || $leaveWithoutPay == null || $temporaryOnleave == null) {
return response()->json(['message' => "Data reason notes not found", 'status' => false]);
}
if ($action == "confirm") {
$this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay);
$this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay, $temporaryOnleave);
return response()->json(['message' => "confirmed", 'status' => true]);
}
@ -887,20 +894,22 @@ class TicketController extends Controller
$dataMasterTypeNotes = CategoryController::getListMasterByType("REASON_NOTES");
$onleave = null;
$leaveWithoutPay = null;
$temporaryOnleave = null;
if ($dataMasterTypeNotes) {
// Get nghỉ phép, nghỉ không phép
$onleave = optional($dataMasterTypeNotes->where('c_code', 'ONLEAVE')->first())->c_code;
$leaveWithoutPay = optional($dataMasterTypeNotes->where('c_code', 'LEAVE_WITHOUT_PAY')->first())->c_code;
$temporaryOnleave = optional($dataMasterTypeNotes->where('c_code', 'TEMPORARY_ONLEAVE')->first())->c_code;
}
// Không tìm được ngày phép, ko phép
if ($onleave == null || $leaveWithoutPay == null) {
if ($onleave == null || $leaveWithoutPay == null || $temporaryOnleave == null) {
return redirect()->to(config('app.client_url') . '/404');
}
if ($action == "confirm") {
$this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay);
$this->handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay, $temporaryOnleave);
return redirect()->to(config('app.client_url') . '/tickets-management');
}
@ -913,7 +922,7 @@ class TicketController extends Controller
return redirect()->to(config('app.client_url') . '/tickets-management');
}
private function handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay)
private function handleConfirmTicket($ticket, $admin, $admin_note, $onleave, $leaveWithoutPay, $temporaryOnleave)
{
$startDate = $ticket->start_date; //Start day
$startPeriod = $ticket->start_period; //The session begins
@ -934,6 +943,7 @@ class TicketController extends Controller
$dataListPeriod = $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period);
$balanceCheckResult = $this->checkLeaveBalance($user, $dataListPeriod, null, true);
// dd($balanceCheckResult, $dataListPeriod);
$currentMonth = Carbon::now()->month;
if ($balanceCheckResult['success'] == false) {
if ($balanceCheckResult['months_info']) {
foreach ($balanceCheckResult['months_info'] as $monthInfo) {
@ -960,7 +970,7 @@ class TicketController extends Controller
'n_month' => $month,
'n_year' => $year,
'n_time_type' => 'S',
'n_reason' => $onleave,
'n_reason' => $month > $currentMonth ? $temporaryOnleave : $onleave,
'n_note' => $ticket->reason,
'ticket_id' => $ticket->id
]);
@ -970,7 +980,7 @@ class TicketController extends Controller
'n_month' => $month,
'n_year' => $year,
'n_time_type' => 'C',
'n_reason' => $leaveWithoutPay,
'n_reason' => $month > $currentMonth ? $temporaryOnleave : $leaveWithoutPay,
'n_note' => $ticket->reason,
'ticket_id' => $ticket->id
]);
@ -985,7 +995,7 @@ class TicketController extends Controller
'n_month' => $month,
'n_year' => $year,
'n_time_type' => $period,
'n_reason' => $onleave,
'n_reason' => $month > $currentMonth ? $temporaryOnleave : $onleave,
'n_note' => $ticket->reason,
'ticket_id' => $ticket->id
]);
@ -999,7 +1009,7 @@ class TicketController extends Controller
'n_month' => $month,
'n_year' => $year,
'n_time_type' => $period,
'n_reason' => $leaveWithoutPay,
'n_reason' => $month > $currentMonth ? $temporaryOnleave : $leaveWithoutPay,
'n_note' => $ticket->reason,
'ticket_id' => $ticket->id
]);
@ -1019,49 +1029,49 @@ class TicketController extends Controller
'n_month' => $month,
'n_year' => $year,
'n_time_type' => $result['period'],
'n_reason' => $onleave, // có phép
'n_reason' => $month > $currentMonth ? $temporaryOnleave : $onleave,
'n_note' => $ticket->reason,
'ticket_id' => $ticket->id
]);
}
}
$yearCheck = Carbon::parse($endDate)->year;
// Check giá trị ld_day_total của bảng leave_days thuộc user id đó với giá trị của list item note trong bảng notes của user id đó
$leaveDaysInfo = LeaveDays::where('ld_user_id', $ticket->user_id)
->where('ld_year', $yearCheck)
->first();
if ($leaveDaysInfo) {
// Tính tổng số ngày nghỉ có phép đã sử dụng trong năm
$totalUsedLeaveDays = Notes::join('categories', function ($join) {
$join->on('notes.n_time_type', '=', 'categories.c_code')
->where('categories.c_type', 'TIME_TYPE');
})
->where('n_user_id', $ticket->user_id)
->where('n_year', $yearCheck)
->where('n_reason', 'ONLEAVE')
->sum('categories.c_value');
// $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 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
// // 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})");
// // 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();
// // 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})");
}
}
// 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) {
@ -1117,7 +1127,8 @@ class TicketController extends Controller
$ticket['status'] = 'CONFIRMED';
$ticket->save();
$this->createOrUpdateRecordForCurrentMonth($month, $year);
$this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
$this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
// Send notification email to users
$data = array(
@ -1358,8 +1369,9 @@ class TicketController extends Controller
$message = "";
if ($index === 0) {
$showMonth = $monthData['month'] > Carbon::now()->month ? $monthData['month'] : Carbon::now()->month;
$message .= "* Quy định: mỗi tháng được nghỉ tối đa {$max} ngày phép\n";
$message .= "- Bạn đang có: {$totalLeave} ngày phép\n\n";
$message .= "- Bạn có: {$totalLeave} ngày phép (Tính tới tháng {$showMonth})\n\n";
}
// Hiển thị cộng phép nếu gửi ticket trong tương lai
@ -1380,7 +1392,7 @@ class TicketController extends Controller
}
if ($willUseLeave > 0 || $willUseNoPay > 0) {
$message .= " - Bạn sẽ sử dụng: ";
$message .= " - Dự kiến bạn sẽ sử dụng: ";
$usedParts = [];
if ($willUseLeave > 0) $usedParts[] = "{$willUseLeave} phép";
if ($willUseNoPay > 0) $usedParts[] = "{$willUseNoPay} không phép";
@ -1478,7 +1490,6 @@ class TicketController extends Controller
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;

View File

@ -171,12 +171,8 @@ class TimekeepingController extends Controller
// Validate the request
$request->validate($rules);
$id = $request->input('id');
$month = $request->month;
$year = $request->year;
$note = Notes::find($id);
if (!$note) {
return response()->json(['message' => 'Note not found', 'status' => false]);
@ -193,77 +189,11 @@ class TimekeepingController extends Controller
$ticket->save();
Notes::where('ticket_id', $ticket->id)->delete();
$this->createOrUpdateRecordForCurrentMonth($month, $year);
// Clear Timekeeping cache
$this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->start_date)->month, Carbon::parse($ticket->start_date)->year);
$this->createOrUpdateRecordForCurrentMonth(Carbon::parse($ticket->end_date)->month, Carbon::parse($ticket->end_date)->year);
return response()->json(['message' => 'Delete success', 'status' => true]);
// $n_month = $note->n_month;
// $n_year = $note->n_year;
// if ($note->n_reason == "ONLEAVE") {
// // Get note reason ONLEAVE by $n_month, $n_year not include $note->id & include $note->n_user_id
// // $onleave = Notes::getNotesByMonthAndYearAndUserId($n_month, $n_year, $note->n_user_id, $note->id);
// // Get note reason LEAVE_WITHOUT_PAY by $n_month, $n_year & include $note->n_user_id
// $leaveWithoutPay = Notes::getNotesByMonthAndYearAndUserIdAndReason($n_month, $n_year, $note->n_user_id, 'LEAVE_WITHOUT_PAY');
// if (count($leaveWithoutPay) > 0) {
// $deletedValue = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
// $needUpdate = $deletedValue;
// // dd($needUpdate, $leaveWithoutPay);
// foreach ($leaveWithoutPay as $lwNote) {
// if ($needUpdate <= 0) break;
// if ($lwNote->n_time_type === 'ALL') {
// if ($needUpdate == 1.0) {
// // Chuyển cả note ALL thành phép
// $lwNote->update(['n_reason' => 'ONLEAVE']);
// $needUpdate = 0;
// break;
// } else { // $needUpdate == 0.5
// // Tách ALL thành 2 note S và C, chuyển S thành phép, C giữ không phép
// Notes::create([
// 'n_user_id' => $lwNote->n_user_id,
// 'n_day' => $lwNote->n_day,
// 'n_month' => $lwNote->n_month,
// 'n_year' => $lwNote->n_year,
// 'n_time_type' => 'S',
// 'n_reason' => 'ONLEAVE',
// 'n_note' => $lwNote->n_note
// ]);
// Notes::create([
// 'n_user_id' => $lwNote->n_user_id,
// 'n_day' => $lwNote->n_day,
// 'n_month' => $lwNote->n_month,
// 'n_year' => $lwNote->n_year,
// 'n_time_type' => 'C',
// 'n_reason' => 'LEAVE_WITHOUT_PAY',
// 'n_note' => $lwNote->n_note
// ]);
// $lwNote->delete();
// $needUpdate = 0;
// break;
// }
// } else {
// // Nếu $lwNote->n_time_type == 'S' hoặc 'C' => 0.5
// if ($needUpdate == 1.0) {
// // Chuyển cả note ALL thành phép
// $lwNote->update(['n_reason' => 'ONLEAVE']);
// $needUpdate -= 0.5;
// } else { // $needUpdate == 0.5
// // S hoặc C, chỉ cần chuyển đúng 0.5 ngày
// $lwNote->update(['n_reason' => 'ONLEAVE']);
// $needUpdate = 0;
// break;
// }
// }
// }
// } else {
// // Khi note phép và k tồn tại nghỉ không phép => phép + dồn cho tháng sau
// }
// }
// $note->delete();
// $this->createOrUpdateRecordForCurrentMonth($month, $year);
// return response()->json(['message' => 'Delete success', 'status' => true]);
}
public function export(Request $request)

View File

@ -21,4 +21,4 @@ class AddMonthlyLeaveDaysCommand extends Command
$year = $this->argument('year');
AddMonthlyLeaveDays::dispatch($month, $year);
}
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Jobs\UpdateTemporaryLeaveDays;
class UpdateTemporaryLeaveDaysCommand extends Command
{
protected $signature = 'update:temporary-leavedays {month?} {year?}';
protected $description = 'Tính lại ngày phép cho các note tạm.';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$month = $this->argument('month');
$year = $this->argument('year');
UpdateTemporaryLeaveDays::dispatch($month, $year);
}
}

View File

@ -34,8 +34,9 @@ class Kernel extends ConsoleKernel
// Chạy buổi chiều lúc 17:30
$schedule->command('attendance:check C')->dailyAt('17:30');
// Chạy vào 00:01 ngày đầu tiên của mỗi tháng
// Chạy vào ngày đầu tiên của mỗi tháng
$schedule->command('add:monthly-leavedays')->monthlyOn(1, '00:01');
$schedule->command('update:temporary-leavedays')->monthlyOn(1, '00:05');
}
/**

View File

@ -65,16 +65,6 @@ class AddMonthlyLeaveDays implements ShouldQueue
$permanentMonth = Carbon::parse($user->permanent_date)->month;
if ($this->month > $leaveDay->ld_day_total - ($permanentDefault - $permanentMonth)) {
$leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
// Xử lý ghi chú
$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;
} else {
// Nếu chưa có ghi chú, gán ghi chú mới
$leaveDay->ld_note = $newNote;
}
$leaveDay->save();
}
}
@ -84,16 +74,6 @@ class AddMonthlyLeaveDays implements ShouldQueue
if ($leaveDay->ld_day_total < $this->month) {
// Cộng mỗi tháng 1 ngày phép cho nhân viên
$leaveDay->ld_day_total += self::ONLEAVE_PER_MONTH;
// Xử lý ghi chú
$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;
} else {
// Nếu chưa có ghi chú, gán ghi chú mới
$leaveDay->ld_note = $newNote;
}
$leaveDay->save();
}
}

View File

@ -0,0 +1,220 @@
<?php
namespace App\Jobs;
use App\Models\LeaveDays;
use App\Models\Notes;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Modules\Admin\app\Models\Category;
class UpdateTemporaryLeaveDays implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $month;
protected $year;
public function __construct($month = null, $year = null)
{
$this->month = $month ?? Carbon::now()->month;
$this->year = $year ?? Carbon::now()->year;
}
/**
* Execute the job.
*/
public function handle()
{
$users = User::get();
foreach ($users as $user) {
$leaveDay = LeaveDays::where('ld_user_id', $user->id)
->where('ld_year', $this->year)
->first();
$notes = Notes::where('n_reason', 'TEMPORARY_ONLEAVE')
->where('n_user_id', $user->id)
->where('n_year', $this->year)
->where('n_month', $this->month)
->whereExists(function ($query) use ($user) {
$query->select(DB::raw(1))
->from('tickets')
->where('tickets.user_id', $user->id)
->where('tickets.status', 'CONFIRMED')
->where('tickets.type', 'ONLEAVE');
})
->get();
$maxDaysPerMonth = $this->getMaxLeaveDaysPerMonth();
// Tổng ngày nghỉ sẽ dùng trong tháng
$willUsedDaysInMonth = 0;
foreach ($notes as $note) {
$willUsedDaysInMonth += $note->n_time_type == 'ALL' ? 1.0 : 0.5;
}
// Tổng phép đang có
$onleaveDaysTotal = $leaveDay->ld_day_total + $leaveDay->ld_additional_day + $leaveDay->ld_special_leave_day;
// Phép đã sử dụng tới tháng hiện tại
$usedOnleaveDaysTotal = 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', $this->year)
->where('n_month', "<=", $this->month)
->where('n_reason', 'ONLEAVE')
->sum('categories.c_value');
// Phép còn lại
$remainingOnleaveDays = $onleaveDaysTotal - $usedOnleaveDaysTotal;
// Log::debug("User {$user->name}\n");
// Log::debug(
// "📊 Thống kê ngày phép:\n" .
// " - Tháng: {$this->month}\n" .
// " - Tổng ngày nghỉ sẽ dùng trong tháng: $willUsedDaysInMonth\n" .
// " - Tổng ngày phép: $onleaveDaysTotal\n" .
// " - Tổng ngày phép đã nghỉ: $usedOnleaveDaysTotal\n" .
// " - Tổng ngày phép còn lại: $remainingOnleaveDays\n"
// );
$onleave_days_will_use = 0; // Ngày phép sẽ dùng
$nopay_days_will_use = 0; // Ngày ko phép sẽ dùng
// Ngày phép còn lại <= 0 (Hết phép)
if ($remainingOnleaveDays <= 0) {
$onleave_days_will_use = 0;
$nopay_days_will_use = $willUsedDaysInMonth;
Log::debug("--- Hết phép trong tháng ---");
}
// Ngày phép còn lại < ngày yêu cầu (Không đủ phép)
else if ($remainingOnleaveDays < $willUsedDaysInMonth) {
// Vượt limit
if ($willUsedDaysInMonth > $maxDaysPerMonth) {
// Phép còn lại > limit
if ($remainingOnleaveDays > $maxDaysPerMonth) {
$onleave_days_will_use = $maxDaysPerMonth;
$nopay_days_will_use = $willUsedDaysInMonth - $maxDaysPerMonth;
}
// Phép còn lại < limit
else {
$onleave_days_will_use = $remainingOnleaveDays;
$nopay_days_will_use = $willUsedDaysInMonth - $remainingOnleaveDays;
}
Log::debug("--- Không đủ phép trong tháng, vượt quá limit ---",);
}
// Không vượt limit
else {
$onleave_days_will_use = $remainingOnleaveDays;
$nopay_days_will_use = $willUsedDaysInMonth - $remainingOnleaveDays;
Log::debug("--- Không đủ phép trong tháng, ko vượt limit ---");
}
}
// Ngày phép còn lại >= ngày yêu cầu (Đủ phép)
else {
// Vượt limit
if ($willUsedDaysInMonth > $maxDaysPerMonth) {
$onleave_days_will_use = $maxDaysPerMonth;
$nopay_days_will_use = $willUsedDaysInMonth - $maxDaysPerMonth;
Log::debug("--- Đủ phép, vượt limit ---");
}
// Không vượt limit
else {
$onleave_days_will_use = $willUsedDaysInMonth;
$nopay_days_will_use = 0;
Log::debug("--- Đủ phép ---");
}
}
Log::debug("", [
"Phep" => $onleave_days_will_use,
"Khong Phep" => $nopay_days_will_use
]);
// Có nghỉ không phép
if ($nopay_days_will_use > 0) {
foreach ($notes as $note) {
$value = ($note->n_time_type === 'ALL') ? 1.0 : 0.5;
if ($note->n_time_type === 'ALL' && $onleave_days_will_use == 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' => $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();
$onleave_days_will_use = 0;
$nopay_days_will_use -= 0.5;
} elseif ($onleave_days_will_use > 0) {
// Dùng ngày phép trước
$use = min($onleave_days_will_use, $value);
$note->update([
'n_reason' => "ONLEAVE"
]);
$onleave_days_will_use -= $use;
} elseif ($nopay_days_will_use > 0) {
// Hết phép, chuyển sang không phép
$use = min($nopay_days_will_use, $value);
$note->update([
'n_reason' => "LEAVE_WITHOUT_PAY"
]);
$nopay_days_will_use -= $use;
}
}
}
// Đủ phép
else {
foreach ($notes as $note) {
$note->update([
'n_reason' => "ONLEAVE"
]);
}
}
}
}
private function getMaxLeaveDaysPerMonth(): int
{
$limitLeaveMonth = Category::where('c_type', 'LIMIT_LEAVE_MONTH')->where('c_code', "LIMIT")->first();
if ($limitLeaveMonth) {
$maxDaysPerMonth = (int)$limitLeaveMonth->c_value;
} else {
$maxDaysPerMonth = 3; // default nếu k có setting
}
return $maxDaysPerMonth;
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
DB::table('categories')->insert([
[
'c_code' => 'TEMPORARY_ONLEAVE',
'c_name' => 'Nghỉ tạm trong tương lai',
'c_type' => 'REASON_NOTES',
'c_value' => "",
'c_active' => 1,
'created_at' => now(),
'updated_at' => now(),
],
]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
DB::table('categories')->where('c_code', 'TEMPORARY_ONLEAVE')->delete();
}
};

View File

@ -21,7 +21,7 @@ import {
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { notifications } from '@mantine/notifications'
import { IconEdit, IconFileExcel } from '@tabler/icons-react'
import { IconEdit, IconFileExcel, IconHelpCircle } from '@tabler/icons-react'
import classes from './LeaveManagement.module.css'
import {
@ -294,14 +294,17 @@ const LeaveManagement = () => {
return monthInYear.map((d, i) => {
let totalOnLeaveMonth = 0
let totalLeaveWithoutPayMonth = 0
let totalTempMonth = 0
monthlyLeaveDays
.filter((item) => item.month === d.value)
.map((item) => {
if (item.reason_code === 'ONLEAVE') {
totalOnLeaveMonth += Number(item.leave_days)
} else {
} else if (item.reason_code === 'LEAVE_WITHOUT_PAY') {
totalLeaveWithoutPayMonth += Number(item.leave_days)
} else {
totalTempMonth += Number(item.leave_days)
}
})
@ -665,9 +668,11 @@ const LeaveManagement = () => {
let onleaveDaysInMonth: MonthlyLeaveDays[] = []
let nopayDaysInMonth: MonthlyLeaveDays[] = []
let tempDaysInMonth: MonthlyLeaveDays[] = []
let totalOnLeaveMonth = 0
let totalLeaveWithoutPayMonth = 0
let totalTempMonth = 0
let usedAdditionalDay = 0
user.monthlyLeaveDays
@ -676,9 +681,12 @@ const LeaveManagement = () => {
if (item.reason_code === 'ONLEAVE') {
totalOnLeaveMonth += Number(item.leave_days)
onleaveDaysInMonth.push(item)
} else {
} else if (item.reason_code === 'LEAVE_WITHOUT_PAY') {
totalLeaveWithoutPayMonth += Number(item.leave_days)
nopayDaysInMonth.push(item)
} else {
totalTempMonth += Number(item.leave_days)
tempDaysInMonth.push(item)
}
})
@ -703,6 +711,7 @@ const LeaveManagement = () => {
return (
<Table.Td
bg={total > 0 ? '#ffb5b5' : ''}
opacity={d.value > currentMonth ? 0.6 : 1}
key={i}
ta={'center'}
>
@ -757,10 +766,51 @@ const LeaveManagement = () => {
</Stack>
</Box>
)}
{totalTempMonth > 0 && (
<Box mt={6}>
<Group align="center">
<Text fw={500} c="blue" size="sm" mb={3}>
Nghỉ dự kiến: {totalTempMonth}
</Text>
<Tooltip
multiline
opened
position="right"
label={
<Text size="xs">
Ngày nghỉ đã đưc duyệt, sẽ tính phép
khi đến tháng nghỉ.
</Text>
}
offset={{ mainAxis: 15 }}
>
<Text fw={500} c="blue">
<IconHelpCircle
width={15}
height={15}
/>
</Text>
</Tooltip>
</Group>
<Stack gap={2} pl="md">
{tempDaysInMonth?.map(
(itemDay: any, indexDay: number) => (
<Text size="xs" key={indexDay}>
{itemDay.time_type_name} (
{itemDay.day}/{itemDay.month})
</Text>
),
)}
</Stack>
</Box>
)}
</Box>
}
>
<p>{total === 0 ? '' : total}</p>
<Text size="sm">{total === 0 ? '' : total}</Text>
</Tooltip>
</Table.Td>
)

View File

@ -79,6 +79,7 @@ const Tickets = () => {
const [item, setItem] = useState({ id: 0 })
const [activeBtn, setActiveBtn] = useState(false)
const [disableBtn, setDisableBtn] = useState(false)
const [isFetchData, setIsFetch] = useState(false)
const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
const [dataReason, setDataReason] = useState<DataReason[]>([])
@ -109,6 +110,8 @@ const Tickets = () => {
useEffect(() => {
const fetchData = async () => {
setIsFetch(true)
const resultTimeType = await getListMasterByType('TIME_TYPE')
setDataTimeType(
resultTimeType.filter((item: DataTimeType) => item.c_code !== 'ALL'),
@ -116,6 +119,8 @@ const Tickets = () => {
const resultReason = await getListMasterByType('REASON')
setDataReason(resultReason)
setIsFetch(false)
}
fetchData()
@ -369,6 +374,7 @@ const Tickets = () => {
setAction('add')
form.reset()
}}
disabled={isFetchData}
>
+ Add
</Button>
@ -554,7 +560,8 @@ const Tickets = () => {
if (confirmValues) {
try {
setConfirmLoading(true)
action === 'add' && (await handleCreate(confirmValues, true))
action === 'add' &&
(await handleCreate(confirmValues, true))
setConfirmLoading(false)
setConfirmModal(false)
} catch (error) {