Hiệu chỉnh add ticket
This commit is contained in:
parent
ddcb78ef98
commit
44fa6b55f7
|
|
@ -220,21 +220,29 @@ class TicketController extends Controller
|
|||
|
||||
$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
|
||||
// Get mảng ngày nghỉ
|
||||
$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]);
|
||||
return AbstractController::ResultError('Không thể tính toán khoảng thời gian nghỉ hợp lệ.');
|
||||
}
|
||||
// Lây thông tin tickets đang ở trạng thái WAITING
|
||||
$ticketsWaiting = Ticket::where('user_id', $user->id)->where('status', 'WAITING')->get();
|
||||
if ($ticketsWaiting->count() > 0) {
|
||||
foreach ($ticketsWaiting as $ticket) {
|
||||
$dataListPeriod = array_merge($dataListPeriod, $this->getAllPeriodNew($ticket->start_date, $ticket->start_period, $ticket->end_date, $ticket->end_period));
|
||||
}
|
||||
}
|
||||
// dd($dataListPeriod);
|
||||
|
||||
// Kiểm tra số dư ngày phép
|
||||
$balanceCheckResult = $this->checkLeaveBalance($user, $currentYear, $dataListPeriod);
|
||||
$balanceCheckResult = $this->checkLeaveBalance($user, $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($balanceCheckResult);
|
||||
return AbstractController::ResultError("Không thỏa mãn điều kiện ngày phép", $balanceCheckResult);
|
||||
}
|
||||
}
|
||||
// --- Kết thúc kiểm tra ---
|
||||
|
|
@ -286,54 +294,24 @@ class TicketController extends Controller
|
|||
* 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
|
||||
private function checkLeaveBalance($user, ?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
|
||||
// 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
|
||||
];
|
||||
}
|
||||
|
|
@ -386,6 +364,7 @@ class TicketController extends Controller
|
|||
$hasInsufficientDays = false;
|
||||
$errorMessage = '';
|
||||
$remainingDaysInMonthIsUsed = 0;
|
||||
|
||||
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');
|
||||
|
|
@ -425,18 +404,27 @@ class TicketController extends Controller
|
|||
if ($remainingDaysInMonthRemaining <= 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.";
|
||||
$monthMessage = "* Hiện tại bạn đã hết phép nghỉ trong tháng {$monthData['month']}/{$monthData['year']}\n - Bạn sẽ nộp: " . $monthData['days_requested'] . " ngày không phép.";
|
||||
$errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
|
||||
} else if ($remainingDaysInMonthRemaining < $monthData['days_requested']) { // không đủ ngày phép
|
||||
$hasInsufficientDays = true;
|
||||
$month_data['status'] = 'insufficient_days';
|
||||
$daysNotEnough = $monthData['days_requested'] - $remainingDaysInMonthRemaining;
|
||||
$monthMessage = "Tháng {$monthData['month']}/{$monthData['year']}: Số ngày phép còn lại: {$remainingDaysInMonthRemaining}, Số ngày yêu cầu: {$monthData['days_requested']}.\nBạn sẽ sử dụng {$remainingDaysInMonthRemaining} ngày phép và {$daysNotEnough} ngày không phép.";
|
||||
$monthMessage = "* Tháng {$monthData['month']}/{$monthData['year']}: \n - Số ngày phép còn lại: {$remainingDaysInMonthRemaining}, Số ngày yêu cầu: {$monthData['days_requested']}.\n - Bạn sẽ sử dụng {$remainingDaysInMonthRemaining} ngày phép và {$daysNotEnough} ngày không phép.";
|
||||
$errorMessage .= $errorMessage ? "\n\n" . $monthMessage : $monthMessage;
|
||||
|
||||
$remainingDaysInMonthIsUsed = $remainingDaysInMonth;
|
||||
}
|
||||
} else if ($remainingDaysInMonthRemaining >= $monthData['days_requested']) { // Đủ ngày phép ở tháng đó
|
||||
|
||||
// 1. Check thêm rule 1 tháng chỉ được nghỉ tối đa $maxDaysPerMonth ngày có phép, ngày vượt sẽ là ngày không phép
|
||||
if ($monthData['days_requested'] > $maxDaysPerMonth) {
|
||||
$hasInsufficientDays = true;
|
||||
$month_data['status'] = 'exceed_max_days';
|
||||
$daysWithoutPermission = $monthData['days_requested'] - $maxDaysPerMonth;
|
||||
$monthMessage = "* Theo quy định ngày phép tôi đa mỗi tháng là {$maxDaysPerMonth} ngày. \nTháng {$monthData['month']}/{$monthData['year']}: \n - Số ngày phép còn lại: {$remainingDaysInMonthRemaining}, Số ngày yêu cầu: {$monthData['days_requested']}.\n - Bạn sẽ sử dụng {$maxDaysPerMonth} ngày phép và {$daysWithoutPermission} 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;
|
||||
}
|
||||
|
|
@ -445,7 +433,7 @@ class TicketController extends Controller
|
|||
if ($hasInsufficientDays) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $errorMessage . "\n\nBạn có chấp nhận không?",
|
||||
'message' => $errorMessage . "\n\nBạn có chấp nhận không?\n",
|
||||
'warning_type' => 'exceed_monthly_limit',
|
||||
'months_info' => $monthsInfo
|
||||
];
|
||||
|
|
@ -480,11 +468,13 @@ class TicketController extends Controller
|
|||
|
||||
$totalAllocated = 0;
|
||||
if ($leaveDaysInfo) {
|
||||
if ($leaveDaysInfo->ld_day_total > $month) {
|
||||
$totalAllocated = $month;
|
||||
} else {
|
||||
$totalAllocated = $leaveDaysInfo->ld_day_total;
|
||||
}
|
||||
// if ($leaveDaysInfo->ld_day_total > $month) {
|
||||
// $totalAllocated = $month;
|
||||
// } else {
|
||||
// $totalAllocated = $leaveDaysInfo->ld_day_total;
|
||||
// }
|
||||
$totalAllocated = $month; //(+ tạm để check)
|
||||
// bên hàm duyệt ticket sẽ check lại để + 1 ngày trước job để đảm bảo đủ ngày phép
|
||||
} else {
|
||||
Log::warning("No LeaveDays record found for user ID: {$user->id}, year: {$year}. Assuming 0 allocated days.");
|
||||
}
|
||||
|
|
@ -529,36 +519,6 @@ class TicketController extends Controller
|
|||
->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 = [
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from '@/api/Admin'
|
||||
import { DataTablePagination } from '@/components/DataTable/DataTable'
|
||||
import { Xdelete, create } from '@/rtk/helpers/CRUD'
|
||||
import { get } from '@/rtk/helpers/apiService'
|
||||
import { get, post } from '@/rtk/helpers/apiService'
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
|
|
@ -26,6 +26,7 @@ import { IconTrash } from '@tabler/icons-react'
|
|||
import moment from 'moment'
|
||||
import { useEffect, useState } from 'react'
|
||||
import classes from './Tickets.module.css'
|
||||
import { _NOTIFICATION_MESS } from '@/rtk/helpers/notificationMess'
|
||||
|
||||
type TTickets = {
|
||||
id: number
|
||||
|
|
@ -82,6 +83,11 @@ const Tickets = () => {
|
|||
const [dataTimeType, setDataTimeType] = useState<DataTimeType[]>([])
|
||||
const [dataReason, setDataReason] = useState<DataReason[]>([])
|
||||
|
||||
const [confirmModal, setConfirmModal] = useState(false)
|
||||
const [confirmMessage, setConfirmMessage] = useState('')
|
||||
const [confirmValues, setConfirmValues] = useState<TTickets | null>(null)
|
||||
const [confirmLoading, setConfirmLoading] = useState(false)
|
||||
|
||||
const getListMasterByType = async (type: string) => {
|
||||
try {
|
||||
const params = {
|
||||
|
|
@ -271,27 +277,61 @@ const Tickets = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleCreate = async (values: TTickets) => {
|
||||
const handleCreate = async (values: TTickets, confirm: boolean = false) => {
|
||||
try {
|
||||
const res = await create(
|
||||
addTicket,
|
||||
{
|
||||
// time_string: moment(values.time_string).format('YYYY-MM-DD HH:mm:ss'),
|
||||
start_date: moment(values.start_date).format('YYYY-MM-DD'),
|
||||
start_period: values.start_period,
|
||||
end_date: moment(values.end_date).format('YYYY-MM-DD'),
|
||||
end_period: values.end_period,
|
||||
type: values.type,
|
||||
reason: values.reason,
|
||||
},
|
||||
getAllTickets,
|
||||
)
|
||||
if (res === true) {
|
||||
let params = {
|
||||
// time_string: moment(values.time_string).format('YYYY-MM-DD HH:mm:ss'),
|
||||
start_date: moment(values.start_date).format('YYYY-MM-DD'),
|
||||
start_period: values.start_period,
|
||||
end_date: moment(values.end_date).format('YYYY-MM-DD'),
|
||||
end_period: values.end_period,
|
||||
type: values.type,
|
||||
reason: values.reason,
|
||||
is_accept: confirm,
|
||||
}
|
||||
|
||||
let res = await post(addTicket, params)
|
||||
|
||||
if (res.status) {
|
||||
notifications.show({
|
||||
title: 'Success',
|
||||
message: _NOTIFICATION_MESS.create_success,
|
||||
color: 'green',
|
||||
})
|
||||
setAction('')
|
||||
form.reset()
|
||||
|
||||
getAllTickets()
|
||||
}
|
||||
if (!res.status && res.errors) {
|
||||
if (!res.data?.success && res.data?.message) {
|
||||
//popup notification confirm or cancel
|
||||
setConfirmMessage(res.data?.message)
|
||||
setConfirmValues(values)
|
||||
setConfirmModal(true)
|
||||
} else {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: (
|
||||
<div style={{ whiteSpace: 'pre-line' }}>
|
||||
{res.message ?? _NOTIFICATION_MESS.create_error}
|
||||
</div>
|
||||
),
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log(res, 'res')
|
||||
// return res.status
|
||||
} catch (error: any) {
|
||||
if (error.response.status === 422) {
|
||||
const errorMess = error.response.data.message
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: errorMess,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,9 +362,7 @@ const Tickets = () => {
|
|||
return (
|
||||
<div>
|
||||
<div className={classes.title}>
|
||||
<h3>
|
||||
Tickets
|
||||
</h3>
|
||||
<h3>Tickets</h3>
|
||||
<Button
|
||||
m={5}
|
||||
onClick={() => {
|
||||
|
|
@ -490,6 +528,56 @@ const Tickets = () => {
|
|||
</Group>
|
||||
</Text>
|
||||
</Dialog>
|
||||
|
||||
{/* Confirm Modal */}
|
||||
<Modal
|
||||
opened={confirmModal}
|
||||
onClose={() => !confirmLoading && setConfirmModal(false)}
|
||||
title={
|
||||
<Text fw={700} fz="lg">
|
||||
Warning
|
||||
</Text>
|
||||
}
|
||||
centered
|
||||
closeOnClickOutside={!confirmLoading}
|
||||
closeOnEscape={!confirmLoading}
|
||||
>
|
||||
<Box p="md">
|
||||
<Text style={{ whiteSpace: 'pre-line' }} mb={20}>
|
||||
{confirmMessage}
|
||||
</Text>
|
||||
<Group justify="center">
|
||||
<Button
|
||||
color="green"
|
||||
loading={confirmLoading}
|
||||
onClick={async () => {
|
||||
if (confirmValues) {
|
||||
try {
|
||||
setConfirmLoading(true)
|
||||
action === 'add' && (await handleCreate(confirmValues, true))
|
||||
setConfirmLoading(false)
|
||||
setConfirmModal(false)
|
||||
} catch (error) {
|
||||
setConfirmLoading(false)
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
<Button
|
||||
color="red"
|
||||
disabled={confirmLoading}
|
||||
onClick={() => {
|
||||
setConfirmModal(false)
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</Group>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export const create = async (
|
|||
if (res.status === false) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: res.message ?? _NOTIFICATION_MESS.create_error,
|
||||
message: <div style={{ whiteSpace: 'pre-line' }}>{res.message ?? _NOTIFICATION_MESS.create_error}</div>,
|
||||
color: 'red',
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue