178 lines
6.0 KiB
PHP
178 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\OvertimeApplication;
|
|
use App\Models\AttendanceRecord;
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Inertia;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
class OvertimeApplicationController extends Controller
|
|
{
|
|
public function getAttendanceDetails(Request $request)
|
|
{
|
|
$request->validate([
|
|
'date' => 'required|date',
|
|
'user_id' => 'nullable|exists:users,id'
|
|
]);
|
|
|
|
$userId = $request->user_id ?: Auth::id();
|
|
|
|
// If not admin/hr, cannot query other users
|
|
if ($request->user_id && $request->user_id != Auth::id()) {
|
|
if (!in_array(Auth::user()->type, ['admin', 'hr', 'company'])) {
|
|
abort(403);
|
|
}
|
|
}
|
|
|
|
$record = AttendanceRecord::with('employee.user', 'employee.shift')
|
|
->whereHas('employee', function($q) use ($userId) {
|
|
$q->where('user_id', $userId);
|
|
})
|
|
->where('date', $request->date)
|
|
->first();
|
|
|
|
if (!$record) {
|
|
return response()->json(['message' => 'No attendance record found for this date.']);
|
|
}
|
|
|
|
if (!$record->clock_in || !$record->clock_out) {
|
|
return response()->json(['message' => 'Attendance record is incomplete (missing clock in/out).']);
|
|
}
|
|
|
|
$emp = $record->employee;
|
|
$shiftStart = $record->shift_start_time ?: ($emp->shift->start_time ?? null);
|
|
$shiftEnd = $record->shift_end_time ?: ($emp->shift->end_time ?? null);
|
|
|
|
if (!$shiftStart || !$shiftEnd) {
|
|
return response()->json(['message' => 'No shift assigned for this record.']);
|
|
}
|
|
|
|
$parseTime = function($timeStr) {
|
|
if (!$timeStr) return 0;
|
|
$parts = explode(':', $timeStr);
|
|
return ($parts[0] * 60) + ($parts[1] ?? 0);
|
|
};
|
|
|
|
$outMins = $parseTime($record->clock_out);
|
|
$sStart = $parseTime($shiftStart);
|
|
$sEnd = $parseTime($shiftEnd);
|
|
|
|
// Night shift logic
|
|
if ($sEnd <= $sStart) {
|
|
$sEnd += 1440;
|
|
if ($outMins < $sStart) $outMins += 1440;
|
|
}
|
|
|
|
$overtimeMins = $outMins - $sEnd;
|
|
|
|
if ($overtimeMins >= 60) {
|
|
$otHours = round($overtimeMins / 60, 1);
|
|
return response()->json([
|
|
'has_overtime' => true,
|
|
'suggested_hours' => $otHours,
|
|
'message' => "Shift ended at {$shiftEnd}, clocked out at {$record->clock_out}. Auto-calculated: {$otHours} hours."
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'has_overtime' => false,
|
|
'message' => "Shift ended at {$shiftEnd}, clocked out at {$record->clock_out}. No significant overtime detected."
|
|
]);
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$user = Auth::user();
|
|
|
|
$query = OvertimeApplication::with('user.employee');
|
|
|
|
// If not admin/hr, only show own requests
|
|
if (!in_array($user->type, ['admin', 'hr', 'company'])) {
|
|
$query->where('user_id', $user->id);
|
|
}
|
|
|
|
$applications = $query->orderBy('date', 'desc')->get();
|
|
|
|
$users = [];
|
|
if (in_array($user->type, ['admin', 'hr', 'company'])) {
|
|
$users = \App\Models\User::where('type', 'employee')->select('id', 'name')->get();
|
|
}
|
|
|
|
return Inertia::render('hr/overtime/index', [
|
|
'applications' => $applications,
|
|
'can_approve' => in_array($user->type, ['admin', 'hr', 'company']),
|
|
'users' => $users
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$request->validate([
|
|
'date' => 'required|date',
|
|
'requested_hours' => 'required|numeric|min:0.1',
|
|
'reason' => 'nullable|string',
|
|
'user_id' => 'nullable|exists:users,id'
|
|
]);
|
|
|
|
$userId = Auth::id();
|
|
|
|
// If an admin/hr specifies a user_id, use it.
|
|
if ($request->has('user_id') && $request->user_id != Auth::id()) {
|
|
if (in_array(Auth::user()->type, ['admin', 'hr', 'company'])) {
|
|
$userId = $request->user_id;
|
|
} else {
|
|
abort(403, 'Unauthorized to apply overtime for another employee.');
|
|
}
|
|
}
|
|
|
|
// Check if already requested for this date
|
|
$exists = OvertimeApplication::where('user_id', $userId)
|
|
->where('date', $request->date)
|
|
->whereIn('status', ['pending', 'approved'])
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
if ($request->wantsJson()) {
|
|
return response()->json(['message' => 'You already have a pending or approved overtime request for this date.'], 422);
|
|
}
|
|
return back()->with('error', 'You already have a pending or approved overtime request for this date.');
|
|
}
|
|
|
|
OvertimeApplication::create([
|
|
'user_id' => $userId,
|
|
'date' => $request->date,
|
|
'requested_hours' => $request->requested_hours,
|
|
'reason' => $request->reason,
|
|
'status' => 'pending'
|
|
]);
|
|
|
|
if ($request->wantsJson()) {
|
|
return response()->json(['message' => 'Overtime application submitted successfully.']);
|
|
}
|
|
return back()->with('success', 'Overtime application submitted successfully.');
|
|
}
|
|
|
|
public function updateStatus(Request $request, OvertimeApplication $overtimeApplication)
|
|
{
|
|
if (!in_array(Auth::user()->type, ['admin', 'hr', 'company'])) {
|
|
abort(403);
|
|
}
|
|
|
|
$request->validate([
|
|
'status' => 'required|in:approved,rejected',
|
|
'approved_hours' => 'nullable|required_if:status,approved|numeric|min:0',
|
|
'admin_remarks' => 'nullable|string'
|
|
]);
|
|
|
|
$overtimeApplication->update([
|
|
'status' => $request->status,
|
|
'approved_hours' => $request->status === 'approved' ? $request->approved_hours : null,
|
|
'admin_remarks' => $request->admin_remarks
|
|
]);
|
|
|
|
return back()->with('success', 'Overtime application ' . $request->status . ' successfully.');
|
|
}
|
|
}
|