Files
HRM-System/app/Http/Controllers/Api/AttendanceApiController.php

233 lines
8.5 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\AttendanceRecord;
use App\Services\AttendanceService;
use App\Services\EmployeeService;
use Illuminate\Http\Request;
use Exception;
class AttendanceApiController extends Controller
{
protected EmployeeService $employeeService;
protected AttendanceService $attendanceService;
public function __construct(EmployeeService $employeeService, AttendanceService $attendanceService)
{
$this->employeeService = $employeeService;
$this->attendanceService = $attendanceService;
}
public function today(Request $request)
{
try {
$employee = $this->employeeService->resolveEmployee($request->user());
} catch (Exception $e) {
return response()->json([
'success' => false,
'data' => null,
'message' => 'Employee profile not found',
'errors' => null,
], 403);
}
$record = $this->attendanceService->getTodayRecord($employee);
$branchId = $employee->branch_id ?? $request->user()->branch_id ?? null;
$creatorId = $request->user()->type === 'company' ? $request->user()->id : $request->user()->created_by;
$activeLocation = \App\Models\AttendanceLocation::where('created_by', $creatorId)
->where('is_active', true)
->where(function ($q) use ($branchId) {
$q->whereNull('branch_id')->orWhere('branch_id', $branchId);
})
->first();
$geofence = null;
if ($activeLocation) {
$geofence = [
'latitude' => (float)$activeLocation->latitude,
'longitude' => (float)$activeLocation->longitude,
'radius' => (int)$activeLocation->radius_meters,
'name' => $activeLocation->name,
];
} else {
$branch = $branchId ? \App\Models\Branch::find($branchId) : null;
if ($branch && isset($branch->latitude, $branch->longitude)) {
$geofence = [
'latitude' => (float)$branch->latitude,
'longitude' => (float)$branch->longitude,
'radius' => (int)($branch->radius ?? 500),
'name' => $branch->name ?? 'Branch',
];
}
}
$formatDateTime = function ($date, $time) {
if (!$date || !$time) return null;
$cleanDate = explode('T', $date)[0];
$cleanDate = explode(' ', $cleanDate)[0];
return "{$cleanDate}T{$time}";
};
$data = [
'clocked_in' => $record ? !!$record->clock_in : false,
'clocked_out' => $record ? !!$record->clock_out : false,
'on_break' => $record ? (!empty($record->break_in) && empty($record->break_out)) : false,
'clock_in_time' => $record ? $formatDateTime($record->date, $record->clock_in) : null,
'clock_out_time' => $record ? $formatDateTime($record->date, $record->clock_out) : null,
'break_in_time' => $record ? $formatDateTime($record->date, $record->break_in) : null,
'break_out_time' => $record ? $formatDateTime($record->date, $record->break_out) : null,
'total_hours' => $record ? (float)($record->total_hours ?? 0) : 0,
'late_hours' => $record ? (float)($record->late_hours ?? 0) : 0,
'early_hours' => $record ? (float)($record->early_hours ?? 0) : 0,
'break_hours' => $record ? (float)($record->break_hours ?? 0) : 0,
'overtime_hours' => $record ? (float)($record->overtime_hours ?? 0) : 0,
'status' => $record->status ?? 'present',
'notes' => $record->notes ?? null,
'geofence' => $geofence,
];
if ($record) {
$data['id'] = $record->id;
$data['date'] = $record->date;
$data['clock_in'] = $record->clock_in;
$data['clock_out'] = $record->clock_out;
$data['break_in'] = $record->break_in;
$data['break_out'] = $record->break_out;
}
return response()->json([
'success' => true,
'data' => $data,
'message' => null,
'errors' => null,
]);
}
public function clock(Request $request)
{
$action = $request->input('action') ?? $request->input('type');
$actionMap = [
'in' => 'clock_in',
'out' => 'clock_out',
'clock_in' => 'clock_in',
'clock_out' => 'clock_out',
'break_in' => 'break_in',
'break_out' => 'break_out',
];
$resolvedAction = $actionMap[$action] ?? $action;
$request->merge(['action' => $resolvedAction]);
$request->validate([
'action' => 'required|string|in:clock_in,clock_out,break_in,break_out',
'latitude' => 'nullable|numeric',
'longitude' => 'nullable|numeric',
'notes' => 'nullable|string',
'activity_note' => 'nullable|string',
'activity' => 'nullable|string',
]);
try {
$employee = $this->employeeService->resolveEmployee($request->user());
} catch (Exception $e) {
return response()->json([
'success' => false,
'data' => null,
'message' => 'Employee profile not found',
'errors' => null,
], 403);
}
$payload = array_merge($request->all(), ['action' => $resolvedAction]);
$result = $this->attendanceService->processClock($request->user(), $employee, $payload);
return response()->json([
'success' => $result['success'],
'data' => $result['data'],
'message' => $result['message'],
'errors' => null,
], $result['status_code']);
}
public function history(Request $request)
{
try {
$employee = $this->employeeService->resolveEmployee($request->user());
} catch (Exception $e) {
return response()->json([
'success' => false,
'data' => [],
'message' => 'Employee profile not found',
'errors' => null,
], 403);
}
$records = AttendanceRecord::where(function ($q) use ($employee, $request) {
$q->where('employee_id', $employee->id)
->orWhere('employee_id', $request->user()->id);
})
->orderBy('date', 'desc')
->limit(30)
->get();
return response()->json([
'success' => true,
'data' => $records,
'message' => null,
'errors' => null,
]);
}
public function summary(Request $request)
{
$todayStr = \Carbon\Carbon::now('Asia/Manila')->format('Y-m-d');
$currentMonth = \Carbon\Carbon::now('Asia/Manila')->format('m');
// Employees on approved leave today
$offEmployees = \App\Models\LeaveApplication::with('user:id,name,avatar')
->where('status', 'approved')
->whereDate('start_date', '<=', $todayStr)
->whereDate('end_date', '>=', $todayStr)
->get()
->map(function ($app) {
return [
'id' => $app->user_id,
'name' => $app->user->name ?? 'Employee',
'avatar' => $app->user->avatar ?? null,
];
})
->unique('id')
->values();
// Employees with birthdays this month
$birthdayEmployees = \App\Models\Employee::with('user:id,name,avatar')
->whereNotNull('date_of_birth')
->whereMonth('date_of_birth', $currentMonth)
->get()
->map(function ($emp) {
return [
'id' => $emp->user_id,
'name' => $emp->user->name ?? $emp->name ?? 'Employee',
'avatar' => $emp->user->avatar ?? null,
'dob' => $emp->date_of_birth,
];
})
->values();
return response()->json([
'success' => true,
'data' => [
'off_today_count' => $offEmployees->count(),
'off_today_list' => $offEmployees,
'birthday_this_month_count' => $birthdayEmployees->count(),
'birthday_this_month_list' => $birthdayEmployees,
],
'message' => null,
'errors' => null,
]);
}
}