# Attendance API (근태관리 API) 규칙 ## 개요 근태관리 API는 테넌트 내 사용자의 출퇴근 및 근태 정보를 관리하는 API입니다. `attendances` 테이블을 사용하며, 상세 출퇴근 정보는 `json_details` 필드에 저장합니다. ## 핵심 모델 ### Attendance - **위치**: `App\Models\Tenants\Attendance` - **역할**: 일별 근태 기록 - **특징**: - `BelongsToTenant` 트레이트 사용 (멀티테넌트 자동 스코핑) - `SoftDeletes` 적용 - `json_details` 필드에 상세 출퇴근 정보 저장 ## 엔드포인트 | Method | Path | 설명 | |--------|------|------| | GET | `/v1/attendances` | 근태 목록 조회 | | GET | `/v1/attendances/{id}` | 근태 상세 조회 | | POST | `/v1/attendances` | 근태 등록 | | PATCH | `/v1/attendances/{id}` | 근태 수정 | | DELETE | `/v1/attendances/{id}` | 근태 삭제 | | DELETE | `/v1/attendances/bulk` | 근태 일괄 삭제 | | POST | `/v1/attendances/check-in` | 출근 기록 | | POST | `/v1/attendances/check-out` | 퇴근 기록 | | GET | `/v1/attendances/monthly-stats` | 월간 통계 | ## 데이터 구조 ### 기본 필드 | 필드 | 타입 | 설명 | |------|------|------| | `id` | int | PK | | `tenant_id` | int | 테넌트 ID | | `user_id` | int | 사용자 ID (FK → users) | | `base_date` | date | 기준 일자 | | `status` | string | 근태 상태 | | `json_details` | json | 상세 출퇴근 정보 | | `remarks` | string | 비고 (500자 제한) | | `created_by` | int | 생성자 | | `updated_by` | int | 수정자 | | `deleted_by` | int | 삭제자 | | `deleted_at` | timestamp | Soft Delete | ### 근태 상태 (status) | 상태 | 설명 | |------|------| | `onTime` | 정상 출근 (기본값) | | `late` | 지각 | | `absent` | 결근 | | `vacation` | 휴가 | | `businessTrip` | 출장 | | `fieldWork` | 외근 | | `overtime` | 야근 | | `remote` | 재택근무 | ### json_details 필드 구조 ```json { "check_in": "09:00:00", "check_out": "18:00:00", "gps_data": { "check_in": { "lat": 37.5665, "lng": 126.9780, "accuracy": 10 }, "check_out": { "lat": 37.5665, "lng": 126.9780, "accuracy": 10 } }, "external_work": { "location": "고객사", "purpose": "미팅", "start_time": "14:00:00", "end_time": "16:00:00" }, "multiple_entries": [ { "in": "09:00:00", "out": "12:00:00" }, { "in": "13:00:00", "out": "18:00:00" } ], "work_minutes": 480, "overtime_minutes": 60, "late_minutes": 30, "early_leave_minutes": 0, "vacation_type": "annual|half|sick" } ``` ### 허용된 json_details 키 ```php $allowedKeys = [ 'check_in', // 출근 시간 (HH:MM:SS) 'check_out', // 퇴근 시간 (HH:MM:SS) 'gps_data', // GPS 데이터 (출퇴근 위치) 'external_work', // 외근 정보 'multiple_entries', // 다중 출퇴근 기록 'work_minutes', // 총 근무 시간 (분) 'overtime_minutes', // 초과 근무 시간 (분) 'late_minutes', // 지각 시간 (분) 'early_leave_minutes',// 조퇴 시간 (분) 'vacation_type', // 휴가 유형 ]; ``` ## 비즈니스 규칙 ### 출근 기록 (check-in) 1. 오늘 기록이 있으면 업데이트, 없으면 새로 생성 2. `check_in` 시간과 GPS 데이터 저장 3. 출근 시간 기준으로 상태 자동 결정 (09:00 기준 지각 판단) ```php // 상태 자동 결정 로직 if ($checkIn > '09:00:00') { $status = 'late'; } else { $status = 'onTime'; } ``` ### 퇴근 기록 (check-out) 1. 오늘 출근 기록이 없으면 에러 반환 2. `check_out` 시간과 GPS 데이터 저장 3. 근무 시간(work_minutes) 자동 계산 ```php // 근무 시간 계산 $checkIn = Carbon::createFromFormat('H:i:s', $jsonDetails['check_in']); $checkOut = Carbon::createFromFormat('H:i:s', $checkOutTime); $jsonDetails['work_minutes'] = $checkOut->diffInMinutes($checkIn); ``` ### 근태 등록 (store) 1. 같은 날 같은 사용자 기록이 있으면 에러 반환 2. `json_details` 직접 전달 또는 개별 필드에서 구성 ```php // json_details 처리 방식 $jsonDetails = isset($data['json_details']) && is_array($data['json_details']) ? $data['json_details'] : $this->buildJsonDetails($data); ``` ### 월간 통계 (monthly-stats) 통계 항목: - 총 근무일수 - 상태별 일수 (정상, 지각, 결근, 휴가, 출장, 외근, 야근, 재택) - 총 근무 시간 (분) - 총 초과 근무 시간 (분) ## 검색/필터 파라미터 | 파라미터 | 타입 | 설명 | |----------|------|------| | `user_id` | int | 사용자 필터 | | `date` | date | 특정 날짜 필터 | | `date_from` | date | 시작 날짜 | | `date_to` | date | 종료 날짜 | | `status` | string | 근태 상태 필터 | | `department_id` | int | 부서 필터 (사용자의 부서) | | `sort_by` | string | 정렬 기준 (기본: base_date) | | `sort_dir` | string | 정렬 방향 (기본: desc) | | `per_page` | int | 페이지당 항목 수 (기본: 20) | ## 관계 (Relationships) ```php public function user(): BelongsTo // 사용자 정보 public function creator(): BelongsTo // 생성자 public function updater(): BelongsTo // 수정자 ``` ## 스코프 (Scopes) ```php $query->onDate('2024-01-15'); // 특정 날짜 $query->betweenDates('2024-01-01', '2024-01-31'); // 날짜 범위 $query->forUser(123); // 특정 사용자 $query->withStatus('late'); // 특정 상태 ``` ## Accessor ```php $attendance->check_in; // json_details['check_in'] $attendance->check_out; // json_details['check_out'] $attendance->gps_data; // json_details['gps_data'] $attendance->external_work; // json_details['external_work'] $attendance->multiple_entries; // json_details['multiple_entries'] $attendance->work_minutes; // json_details['work_minutes'] $attendance->overtime_minutes; // json_details['overtime_minutes'] $attendance->late_minutes; // json_details['late_minutes'] $attendance->early_leave_minutes;// json_details['early_leave_minutes'] $attendance->vacation_type; // json_details['vacation_type'] ``` ## 주의사항 1. **중복 방지**: 같은 날짜 + 같은 사용자 조합은 유일해야 함 2. **멀티테넌트**: BelongsToTenant 트레이트로 자동 스코핑 3. **Soft Delete**: deleted_by 기록 후 삭제 4. **Audit**: created_by/updated_by 자동 기록 5. **시간 형식**: check_in/check_out은 HH:MM:SS 형식 6. **표준 출근 시간**: 기본 09:00:00 (회사별 설정 필요)