161 lines
6.0 KiB
PHP
161 lines
6.0 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
use App\Models\Employee;
|
|
use App\Models\AttendanceRecord;
|
|
use App\Models\AttendanceBreak;
|
|
use App\Services\AttendanceService;
|
|
use Tests\TestCase;
|
|
|
|
uses(TestCase::class);
|
|
|
|
test('unified attendance process clock handles full lifecycle across channels', function () {
|
|
updateSetting('working_days', json_encode([0,1,2,3,4,5,6]));
|
|
|
|
$user = User::where('email', 'admin@example.com')->orWhere('type', 'company')->first() ?? User::first();
|
|
$employee = Employee::where('user_id', $user->id)->first() ?? Employee::first();
|
|
|
|
AttendanceRecord::where('employee_id', $user->id)->orWhere('employee_id', $employee->user_id)->delete();
|
|
|
|
$service = new AttendanceService();
|
|
|
|
// 1. Web Clock In
|
|
$resIn = $service->processClock($user, $employee, [
|
|
'action' => 'clock_in',
|
|
'source' => 'web',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
expect($resIn['success'])->toBeTrue();
|
|
|
|
// 2. Mobile Break In
|
|
$resBreakIn = $service->processClock($user, $employee, [
|
|
'action' => 'break_in',
|
|
'source' => 'mobile',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
expect($resBreakIn['success'])->toBeTrue();
|
|
|
|
// 3. Mobile Break Out
|
|
$resBreakOut = $service->processClock($user, $employee, [
|
|
'action' => 'break_out',
|
|
'source' => 'mobile',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
expect($resBreakOut['success'])->toBeTrue();
|
|
|
|
// 4. Web Clock Out
|
|
$resOut = $service->processClock($user, $employee, [
|
|
'action' => 'clock_out',
|
|
'source' => 'web',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
expect($resOut['success'])->toBeTrue();
|
|
|
|
$rec = AttendanceRecord::where('employee_id', $user->id)->whereDate('date', date('Y-m-d'))->first();
|
|
expect($rec)->not->toBeNull();
|
|
expect($rec->clock_in_source)->toBe('web');
|
|
expect($rec->clock_out_source)->toBe('web');
|
|
|
|
$breaksCount = AttendanceBreak::where('attendance_record_id', $rec->id)->count();
|
|
expect($breaksCount)->toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
test('ATT-MOB-WEB-001: mobile clock-in followed by web clock-out creates unified record with source metadata', function () {
|
|
updateSetting('working_days', json_encode([0,1,2,3,4,5,6]));
|
|
|
|
$user = User::where('email', 'admin@example.com')->orWhere('type', 'company')->first() ?? User::first();
|
|
$employee = Employee::where('user_id', $user->id)->first() ?? Employee::first();
|
|
|
|
AttendanceRecord::where('employee_id', $user->id)->orWhere('employee_id', $employee->user_id)->delete();
|
|
|
|
$service = new AttendanceService();
|
|
|
|
// Mobile Clock In
|
|
$resIn = $service->processClock($user, $employee, [
|
|
'action' => 'clock_in',
|
|
'source' => 'mobile',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
expect($resIn['success'])->toBeTrue();
|
|
|
|
// Web Clock Out
|
|
$resOut = $service->processClock($user, $employee, [
|
|
'action' => 'clock_out',
|
|
'source' => 'web',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
expect($resOut['success'])->toBeTrue();
|
|
|
|
$rec = AttendanceRecord::where('employee_id', $user->id)->whereDate('date', date('Y-m-d'))->first();
|
|
expect($rec)->not->toBeNull();
|
|
expect($rec->clock_in_source)->toBe('mobile');
|
|
expect($rec->clock_out_source)->toBe('web');
|
|
expect($rec->shift_id)->not->toBeNull();
|
|
});
|
|
|
|
test('web calendar query payload includes mobile record and clock_in_source', function () {
|
|
updateSetting('working_days', json_encode([0,1,2,3,4,5,6]));
|
|
|
|
$user = User::where('email', 'admin@example.com')->orWhere('type', 'company')->first() ?? User::first();
|
|
$employee = Employee::where('user_id', $user->id)->first() ?? Employee::first();
|
|
|
|
AttendanceRecord::where('employee_id', $user->id)->orWhere('employee_id', $employee->user_id)->delete();
|
|
|
|
$service = new AttendanceService();
|
|
$service->processClock($user, $employee, [
|
|
'action' => 'clock_in',
|
|
'source' => 'mobile',
|
|
'latitude' => 14.5995,
|
|
'longitude' => 120.9842,
|
|
]);
|
|
|
|
$controller = new \App\Http\Controllers\AttendanceRecordController();
|
|
$request = \Illuminate\Http\Request::create('/hr/attendance-records/calendar', 'GET', [
|
|
'month' => date('n'),
|
|
'year' => date('Y'),
|
|
]);
|
|
|
|
// Authenticate as user
|
|
$this->actingAs($user);
|
|
|
|
$response = $controller->calendar($request);
|
|
expect($response)->not->toBeNull();
|
|
});
|
|
|
|
test('duplicate clock in is rejected with 422', function () {
|
|
updateSetting('working_days', json_encode([0,1,2,3,4,5,6]));
|
|
|
|
$user = User::where('email', 'admin@example.com')->orWhere('type', 'company')->first() ?? User::first();
|
|
$employee = Employee::where('user_id', $user->id)->first() ?? Employee::first();
|
|
|
|
AttendanceRecord::where('employee_id', $user->id)->orWhere('employee_id', $employee->user_id)->delete();
|
|
|
|
$service = new AttendanceService();
|
|
$res1 = $service->processClock($user, $employee, ['action' => 'clock_in', 'source' => 'mobile', 'latitude' => 14.5995, 'longitude' => 120.9842]);
|
|
expect($res1['success'])->toBeTrue();
|
|
|
|
$res2 = $service->processClock($user, $employee, ['action' => 'clock_in', 'source' => 'mobile', 'latitude' => 14.5995, 'longitude' => 120.9842]);
|
|
expect($res2['success'])->toBeFalse();
|
|
expect($res2['status_code'])->toBe(422);
|
|
});
|
|
|
|
test('clock out without clock in is rejected with 422', function () {
|
|
updateSetting('working_days', json_encode([0,1,2,3,4,5,6]));
|
|
|
|
$user = User::where('email', 'admin@example.com')->orWhere('type', 'company')->first() ?? User::first();
|
|
$employee = Employee::where('user_id', $user->id)->first() ?? Employee::first();
|
|
|
|
AttendanceRecord::where('employee_id', $user->id)->orWhere('employee_id', $employee->user_id)->delete();
|
|
|
|
$service = new AttendanceService();
|
|
$res = $service->processClock($user, $employee, ['action' => 'clock_out', 'source' => 'mobile', 'latitude' => 14.5995, 'longitude' => 120.9842]);
|
|
expect($res['success'])->toBeFalse();
|
|
expect($res['status_code'])->toBe(422);
|
|
});
|