893 lines
37 KiB
PHP
893 lines
37 KiB
PHP
<?php
|
|
|
|
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;
|
|
use App\Traits\HasFilterRequest;
|
|
use App\Traits\HasOrderByRequest;
|
|
use App\Traits\HasSearchRequest;
|
|
use Carbon\Carbon;
|
|
use Carbon\CarbonPeriod;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Mail;
|
|
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
|
|
{
|
|
use HasOrderByRequest;
|
|
use HasFilterRequest;
|
|
use HasSearchRequest;
|
|
use AnalyzeData;
|
|
|
|
public function getByUserId(Request $request)
|
|
{
|
|
$tickets = new Ticket;
|
|
|
|
// Order by
|
|
$this->orderByRequest($tickets, $request);
|
|
|
|
// Filter
|
|
$this->filterRequest(
|
|
builder: $tickets,
|
|
request: $request,
|
|
filterKeys: [
|
|
'type' => self::F_TEXT,
|
|
'reason' => self::F_TEXT,
|
|
'updated_by' => self::F_TEXT,
|
|
'start_date' => [
|
|
'type' => self::F_THAN_EQ_DATETIME,
|
|
'column' => 'tickets.start_date'
|
|
],
|
|
'end_date' => [
|
|
'type' => self::F_THAN_EQ_DATETIME,
|
|
'column' => 'tickets.end_date'
|
|
],
|
|
]
|
|
);
|
|
|
|
$this->searchRequest(
|
|
builder: $tickets,
|
|
value: $request->get('search'),
|
|
fields: [
|
|
'users.name',
|
|
"start_date",
|
|
"startPeriod.c_name",
|
|
"end_date",
|
|
"endPeriod.c_name",
|
|
"typeReason.c_name",
|
|
'status',
|
|
"reason",
|
|
]
|
|
);
|
|
|
|
$responseData = array_merge(
|
|
$tickets
|
|
->join('users', 'tickets.user_id', '=', 'users.id')
|
|
->leftJoin("categories as startPeriod", function ($join) {
|
|
$join->on('start_period', '=', 'startPeriod.c_code');
|
|
$join->on('startPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
|
|
})
|
|
->leftJoin("categories as endPeriod", function ($join) {
|
|
$join->on('end_period', '=', 'endPeriod.c_code');
|
|
$join->on('endPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
|
|
})
|
|
->leftJoin("categories as typeReason", function ($join) {
|
|
$join->on('type', '=', 'typeReason.c_code');
|
|
$join->on('typeReason.c_type', DB::raw("CONCAT('REASON')"));
|
|
})
|
|
->leftJoin("categories as statusTickets", function ($join) {
|
|
$join->on('status', '=', 'statusTickets.c_code');
|
|
$join->on('statusTickets.c_type', DB::raw("CONCAT('STATUS_TICKETS')"));
|
|
})
|
|
->where('user_id', auth('admins')->user()->id)->orderBy('tickets.created_at', 'desc')
|
|
->select(
|
|
'users.name as user_name',
|
|
'users.email',
|
|
'tickets.*',
|
|
'startPeriod.c_name as startPeriodName',
|
|
'endPeriod.c_name as endPeriodName',
|
|
'typeReason.c_name as typeReasonName',
|
|
'statusTickets.c_name as statusTicketsName',
|
|
)
|
|
->paginate($request->get('per_page'))->toArray(),
|
|
['status' => true]
|
|
);
|
|
|
|
return response()->json($responseData);
|
|
}
|
|
|
|
public function getAll(Request $request)
|
|
{
|
|
$tickets = new Ticket;
|
|
|
|
// Order by
|
|
$this->orderByRequest($tickets, $request);
|
|
|
|
// Filter
|
|
$this->filterRequest(
|
|
builder: $tickets,
|
|
request: $request,
|
|
filterKeys: [
|
|
'type' => self::F_TEXT,
|
|
'reason' => self::F_TEXT,
|
|
'updated_by' => self::F_TEXT,
|
|
'start_date' => [
|
|
'type' => self::F_THAN_EQ_DATETIME,
|
|
'column' => 'tickets.start_date'
|
|
],
|
|
'end_date' => [
|
|
'type' => self::F_THAN_EQ_DATETIME,
|
|
'column' => 'tickets.end_date'
|
|
],
|
|
]
|
|
);
|
|
|
|
$this->searchRequest(
|
|
builder: $tickets,
|
|
value: $request->get('search'),
|
|
fields: [
|
|
'users.name',
|
|
"start_date",
|
|
"startPeriod.c_name",
|
|
"end_date",
|
|
"endPeriod.c_name",
|
|
"typeReason.c_name",
|
|
'status',
|
|
"reason",
|
|
"admin_note"
|
|
]
|
|
);
|
|
|
|
if ($request->typeReason != '') {
|
|
$tickets = $tickets->where('tickets.type', '=', $request->typeReason);
|
|
}
|
|
if ($request->statusFilter != '') {
|
|
$tickets = $tickets->where('tickets.status', '=', $request->statusFilter);
|
|
}
|
|
|
|
$responseData = array_merge(
|
|
$tickets
|
|
->join('users', 'tickets.user_id', '=', 'users.id')
|
|
->leftJoin("categories as startPeriod", function ($join) {
|
|
$join->on('start_period', '=', 'startPeriod.c_code');
|
|
$join->on('startPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
|
|
})
|
|
->leftJoin("categories as endPeriod", function ($join) {
|
|
$join->on('end_period', '=', 'endPeriod.c_code');
|
|
$join->on('endPeriod.c_type', DB::raw("CONCAT('TIME_TYPE')"));
|
|
})
|
|
->leftJoin("categories as typeReason", function ($join) {
|
|
$join->on('type', '=', 'typeReason.c_code');
|
|
$join->on('typeReason.c_type', DB::raw("CONCAT('REASON')"));
|
|
})
|
|
->leftJoin("categories as statusTickets", function ($join) {
|
|
$join->on('status', '=', 'statusTickets.c_code');
|
|
$join->on('statusTickets.c_type', DB::raw("CONCAT('STATUS_TICKETS')"));
|
|
})
|
|
->select(
|
|
'users.name as user_name',
|
|
'users.email',
|
|
'tickets.*',
|
|
'startPeriod.c_name as startPeriodName',
|
|
'endPeriod.c_name as endPeriodName',
|
|
'typeReason.c_name as typeReasonName',
|
|
'statusTickets.c_name as statusTicketsName',
|
|
)
|
|
->orderBy('tickets.created_at', 'desc')->paginate($request->get('per_page'))->toArray(),
|
|
['status' => true]
|
|
);
|
|
|
|
return response()->json($responseData);
|
|
}
|
|
|
|
public function createTicket(Request $request)
|
|
{
|
|
// Define validation rules
|
|
$rules = [
|
|
'start_date' => 'required|date',
|
|
'start_period' => 'required|string',
|
|
'end_date' => 'required|date|after_or_equal:start_date',
|
|
'end_period' => 'required|string',
|
|
'type' => 'required|string',
|
|
];
|
|
|
|
// Validate the request
|
|
$request->validate($rules);
|
|
// return $request;
|
|
|
|
// 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
|
|
|
|
$start_date = Carbon::create($startDate)->setTimezone(env('TIME_ZONE'));
|
|
$end_date = Carbon::create($endDate)->setTimezone(env('TIME_ZONE'));
|
|
$currentYear = $start_date->year;
|
|
|
|
// --- 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]);
|
|
}
|
|
|
|
// 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 ---
|
|
|
|
// 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' => $start_date->toDateString(),
|
|
'start_period' => $startPeriod,
|
|
'end_date' => $end_date->toDateString(),
|
|
'end_period' => $endPeriod,
|
|
'type' => $type,
|
|
'status' => 'WAITING',
|
|
'reason' => $reason,
|
|
'user_id' => $user->id
|
|
]);
|
|
|
|
// 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" => 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ỉ có phép 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');
|
|
}
|
|
|
|
//Tính tổng giới hạn ngày nghỉ có phép tối đa trong tháng
|
|
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;
|
|
}
|
|
|
|
private function checkMonthlyLeaveLimit($user, array $dataListPeriod): array
|
|
{
|
|
// Danh sách ngày nghỉ theo tháng
|
|
$requestMonths = $this->groupLeaveRequestsByMonth($dataListPeriod);
|
|
$monthsInfo = [];
|
|
$hasInsufficientDays = false;
|
|
$errorMessage = '';
|
|
|
|
foreach ($requestMonths as $monthKey => $monthData) {
|
|
// Tính tổng số ngày nghỉ có phép trong tháng
|
|
$usedDaysInMonth = $this->getUsedLeaveDaysInMonth($user, $monthData['year'], $monthData['month'], 'ONLEAVE');
|
|
|
|
// Tính tổng số ngày nghỉ không phép trong tháng
|
|
$usedDaysInMonthWithoutPay = $this->getUsedLeaveDaysInMonth($user, $monthData['year'], $monthData['month'], 'LEAVE_WITHOUT_PAY');
|
|
|
|
// Tính tổng giới hạn ngày nghỉ có phép tối đa trong tháng
|
|
$maxDaysPerMonth = $this->getMaxLeaveDaysPerMonth();
|
|
|
|
// Tính tổng số ngày nghỉ trong tháng
|
|
$totalDaysInMonth = $usedDaysInMonth + $usedDaysInMonthWithoutPay + $monthData['days_requested'];
|
|
|
|
// Tính tổng phép có trong tháng
|
|
$totalLeaveDaysInMonth = $this->getTotalLeaveDaysInMonth($user, $monthData['year'], $monthData['month']);
|
|
|
|
// Tính tổng số ngày nghỉ có phép đến tháng hiện tại
|
|
$totalLeaveDaysInMonthToMonth = $this->getTotalLeaveDaysInMonthToMonth($user, $monthData['year'], $monthData['month']);
|
|
|
|
//Ngày phép còn lại trong tháng
|
|
$remainingDaysInMonth = $totalLeaveDaysInMonth - $totalLeaveDaysInMonthToMonth;
|
|
|
|
$month_data = [
|
|
'year' => $monthData['year'],
|
|
'month' => $monthData['month'],
|
|
'total_leave_days_in_month' => $totalLeaveDaysInMonth, //tổng số ngày phép
|
|
'total_leave_days_in_month_to_month' => $totalLeaveDaysInMonthToMonth, // tổng ngày nghỉ có phép
|
|
'remaining_days_in_month' => $remainingDaysInMonth, //ngày phép còn lại
|
|
'days_used' => $usedDaysInMonth, //tổng số ngày nghỉ có phép ở tháng hiện tại
|
|
'days_used_without_pay' => $usedDaysInMonthWithoutPay, //tổng số ngày nghỉ không phép ở tháng hiện tại
|
|
'days_requested' => $monthData['days_requested'], //số ngày yêu cầu nghỉ của tháng
|
|
'status' => 'ok', // mặc định là ok
|
|
];
|
|
|
|
// Xử lý các trường hợp thiếu ngày phép
|
|
if ($remainingDaysInMonth <= 0) { //hết phép
|
|
$hasInsufficientDays = true;
|
|
$month_data['status'] = 'no_days_left';
|
|
$monthMessage = "Hiện tại bạn đã hết phép nghỉ trong tháng {$monthData['month']}/{$monthData['year']}\nBạn sẽ nộp: " . $monthData['days_requested'] . " ngày không phép.";
|
|
$errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
|
|
} else if ($remainingDaysInMonth < $monthData['days_requested']) { // không đủ ngày phép
|
|
$hasInsufficientDays = true;
|
|
$month_data['status'] = 'insufficient_days';
|
|
$daysNotEnough = $monthData['days_requested'] - $remainingDaysInMonth;
|
|
$monthMessage = "Tháng {$monthData['month']}/{$monthData['year']}: Số ngày phép còn lại: {$remainingDaysInMonth}, Số ngày yêu cầu: {$monthData['days_requested']}.\nBạn sẽ sử dụng {$remainingDaysInMonth} ngày phép và {$daysNotEnough} ngày không phép.";
|
|
$errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
|
|
}
|
|
|
|
// Thêm thông tin tháng vào mảng kết quả
|
|
$monthsInfo[] = $month_data;
|
|
}
|
|
|
|
// Trả về kết quả tổng hợp
|
|
if ($hasInsufficientDays) {
|
|
return [
|
|
'success' => false,
|
|
'message' => $errorMessage . "\n\nBạn có chấp nhận không?",
|
|
'warning_type' => 'exceed_monthly_limit',
|
|
'months_info' => $monthsInfo
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => "Đủ ngày phép cho tất cả tháng yêu cầu.",
|
|
'months_info' => $monthsInfo
|
|
];
|
|
}
|
|
|
|
//Tính tổng số ngày nghỉ có phép đến tháng hiện tại
|
|
private function getTotalLeaveDaysInMonthToMonth($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 getTotalLeaveDaysInMonth($user, int $year, int $month): float
|
|
{
|
|
$leaveDaysInfo = LeaveDays::where('ld_user_id', $user->id)
|
|
->where('ld_year', $year)
|
|
->first();
|
|
|
|
$totalAllocated = 0;
|
|
if ($leaveDaysInfo) {
|
|
if ($leaveDaysInfo->ld_day_total > $month) {
|
|
$totalAllocated = $month;
|
|
} else {
|
|
$totalAllocated = $leaveDaysInfo->ld_day_total;
|
|
}
|
|
} else {
|
|
Log::warning("No LeaveDays record found for user ID: {$user->id}, year: {$year}. Assuming 0 allocated days.");
|
|
}
|
|
$totalAllocated = $totalAllocated + $leaveDaysInfo->ld_additional_day + $leaveDaysInfo->ld_special_leave_day;
|
|
return $totalAllocated;
|
|
}
|
|
|
|
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, string $reason): 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', $reason)
|
|
->sum('categories.c_value');
|
|
}
|
|
|
|
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?",
|
|
'warning_type' => 'insufficient_leave_days',
|
|
'month_data' => []
|
|
];
|
|
}
|
|
|
|
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 = [
|
|
'ticket_id' => 'required'
|
|
];
|
|
|
|
// Validate the request
|
|
$request->validate($rules);
|
|
|
|
$user = auth('admins')->user();
|
|
$ticket_id = $request->input('ticket_id');
|
|
$ticket = Ticket::find($ticket_id);
|
|
if ($ticket) {
|
|
// $user->id == user_id of ticket ---> delete
|
|
if ($ticket->user_id == $user->id) {
|
|
$ticket->delete();
|
|
return response()->json(['message' => 'delete success', 'status' => true]);
|
|
} else {
|
|
return response()->json(['message' => 'You are committing an act of vandalism', 'status' => false]);
|
|
}
|
|
}
|
|
|
|
return response()->json(['message' => 'Delete fail', 'status' => false]);
|
|
}
|
|
|
|
public function handleTicket(Request $request)
|
|
{
|
|
|
|
$rules = [
|
|
'ticket_id' => 'required',
|
|
'action' => 'required',
|
|
// 'admin_note' => 'required'
|
|
];
|
|
|
|
// Validate the request
|
|
$request->validate($rules);
|
|
|
|
$ticket_id = $request->input('ticket_id');
|
|
$admin_note = $request->input('admin_note');
|
|
$action = $request->input('action'); // 'confirm' or 'refuse'
|
|
$admin = auth('admins')->user();
|
|
$ticket = Ticket::find($ticket_id);
|
|
|
|
if (!$ticket || $ticket->status !== "WAITING") {
|
|
return response()->json(['message' => "Ticket not found", 'status' => false]);
|
|
}
|
|
$results = $this->getAllPeriod($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period);
|
|
|
|
// $admin->id != user_id of ticket ---> continue
|
|
// Confirm
|
|
// Add records to the notes table like function Timekeeping.addNoteForUser() based on the $results array
|
|
|
|
// Update updated_by and admin_note in tickets table
|
|
|
|
// Send notification email to users
|
|
|
|
// Refuse
|
|
// Update updated_by and admin_note in tickets table
|
|
$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 ($action == "confirm") {
|
|
foreach ($results 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
|
|
]);
|
|
|
|
if ($ticket->type == "WFH") {
|
|
$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')
|
|
]
|
|
]);
|
|
}
|
|
}
|
|
|
|
$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));
|
|
|
|
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));
|
|
return response()->json(['message' => "refused", 'status' => true]);
|
|
}
|
|
|
|
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
|
|
$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();
|
|
$morning = null;
|
|
$afternoon = null;
|
|
$all_day = null;
|
|
|
|
foreach ($time_type as $item) {
|
|
switch ($item->c_code) {
|
|
case 'S':
|
|
$morning = $item;
|
|
break;
|
|
case 'C':
|
|
$afternoon = $item;
|
|
break;
|
|
case 'ALL':
|
|
$all_day = $item;
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach ($period as $date) {
|
|
//If it is the start date
|
|
if ($date->isSameDay($startDate)) {
|
|
//Add start session
|
|
//If the start date is morning, add afternoon
|
|
if ($startDate == $endDate) {
|
|
if ($startPeriod == $endPeriod) {
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $startPeriod];
|
|
} else {
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
|
|
}
|
|
} else {
|
|
if ($startPeriod == $morning->c_code) {
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
|
|
} else {
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $startPeriod];
|
|
}
|
|
}
|
|
} elseif ($date->isSameDay($endDate)) {
|
|
//If it is the end date
|
|
//If the end session is afternoon, add morning first
|
|
|
|
if ($endPeriod == $afternoon->c_code) {
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
|
|
} else {
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $endPeriod];
|
|
}
|
|
} else {
|
|
//Add both sessions for days between intervals
|
|
$results[] = ['date' => $date->toDateString(), 'period' => $all_day->c_code];
|
|
// $results[] = ['date' => $date->toDateString(), 'period' => 'afternoon'];
|
|
}
|
|
}
|
|
|
|
//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;
|
|
}
|
|
}
|