Files
sam-docs/rules/employee-api.md
김보곤 5517b7f04d docs: [esign] 근로계약서 최신 연봉정보 반영 문서화
- features/esign/README.md: 근로계약서 사원 연동 섹션 추가
- projects/e-sign/changelog.md: v1.1.1 변경 이력 추가
- rules/employee-api.md: 전자계약 연동 참조 추가
- dev/changes/20260311: esign 연봉정보 개선 내용 추가
2026-03-11 17:04:27 +09:00

249 lines
7.8 KiB
Markdown

# Employee API (사원관리 API) 규칙
## 개요
사원관리 API는 테넌트 내 사원 정보를 관리하는 API입니다.
`users` 테이블과 `tenant_user_profiles` 테이블을 조합하여 사원 정보를 구성합니다.
## 핵심 모델
### TenantUserProfile
- **위치**: `App\Models\Tenants\TenantUserProfile`
- **역할**: 테넌트별 사용자 프로필 (사원 정보)
- **특징**: `json_extra` 필드에 사원 상세 정보 저장
### User
- **위치**: `App\Models\Members\User`
- **역할**: 기본 사용자 계정 (이름, 이메일, 비밀번호)
## 엔드포인트
| Method | Path | 설명 |
|--------|------|------|
| GET | `/v1/employees` | 사원 목록 조회 |
| GET | `/v1/employees/{id}` | 사원 상세 조회 |
| POST | `/v1/employees` | 사원 등록 |
| PATCH | `/v1/employees/{id}` | 사원 수정 |
| DELETE | `/v1/employees/{id}` | 사원 삭제 (상태 변경) |
| DELETE | `/v1/employees/bulk` | 사원 일괄 삭제 |
| GET | `/v1/employees/stats` | 사원 통계 |
| POST | `/v1/employees/{id}/account` | 시스템 계정 생성 |
## 데이터 구조
### 기본 필드 (TenantUserProfile)
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | int | 테넌트 ID |
| `user_id` | int | 사용자 ID (FK → users) |
| `department_id` | int | 부서 ID (nullable) |
| `position_key` | string | 직위 코드 |
| `job_title_key` | string | 직책 코드 |
| `work_location_key` | string | 근무지 코드 |
| `employment_type_key` | string | 고용 형태 코드 |
| `employee_status` | string | 고용 상태 (active/leave/resigned) |
| `manager_user_id` | int | 상위 관리자 ID (nullable) |
| `profile_photo_path` | string | 프로필 사진 경로 |
| `display_name` | string | 표시명 |
| `json_extra` | json | 확장 사원 정보 |
### json_extra 필드 구조
```json
{
"employee_code": "EMP001",
"resident_number": "encrypted_value",
"gender": "male|female",
"address": "서울시 강남구...",
"salary": 5000000,
"hire_date": "2024-01-15",
"rank": "대리",
"bank_account": {
"bank": "국민은행",
"account": "123-456-789",
"holder": "홍길동"
},
"work_type": "regular|contract|part_time",
"contract_info": {
"start_date": "2024-01-15",
"end_date": "2025-01-14"
},
"emergency_contact": {
"name": "김부모",
"phone": "010-1234-5678",
"relation": "부모"
},
"education": [],
"certifications": []
}
```
### 허용된 json_extra 키
```php
$allowedKeys = [
'employee_code', // 사원번호
'resident_number', // 주민등록번호 (암호화 필수)
'gender', // 성별
'address', // 주소
'salary', // 급여
'hire_date', // 입사일
'rank', // 직급
'bank_account', // 급여계좌
'work_type', // 근무유형
'contract_info', // 계약 정보
'emergency_contact', // 비상연락처
'education', // 학력
'certifications', // 자격증
];
```
## 비즈니스 규칙
### 사원 등록 (store)
1. `users` 테이블에 사용자 생성
2. `user_tenants` pivot에 관계 추가 (is_default: true)
3. `tenant_user_profiles` 생성
4. `json_extra`에 사원 정보 설정
```php
// 자동 생성되는 user_id 형식
$userId = strtolower(explode('@', $email)[0] . '_' . Str::random(4));
```
### 사원 삭제 (destroy)
- **Hard Delete 하지 않음**
- `employee_status``resigned`로 변경
- 사용자 계정은 유지됨
### 사원 상태 (employee_status)
| 상태 | 설명 |
|------|------|
| `active` | 재직 중 |
| `leave` | 휴직 |
| `resigned` | 퇴사 |
### 시스템 계정 (has_account)
- 시스템 계정 = `users.password`가 NULL이 아닌 경우
- `POST /employees/{id}/account`로 비밀번호 설정 시 계정 생성
- 첫 로그인 시 비밀번호 변경 필요 (`must_change_password: true`)
## 검색/필터 파라미터
| 파라미터 | 타입 | 설명 |
|----------|------|------|
| `q` | string | 이름/이메일/사원코드 검색 |
| `status` | string | 고용 상태 필터 |
| `department_id` | int | 부서 필터 |
| `has_account` | bool | 시스템 계정 보유 여부 |
| `sort_by` | string | 정렬 기준 (기본: created_at) |
| `sort_dir` | string | 정렬 방향 (asc/desc) |
| `per_page` | int | 페이지당 항목 수 (기본: 20) |
## 관계 (Relationships)
```php
// TenantUserProfile
public function user(): BelongsTo // 기본 사용자 정보
public function department(): BelongsTo // 소속 부서
public function manager(): BelongsTo // 상위 관리자
```
## 스코프 (Scopes)
```php
$query->active(); // employee_status = 'active'
$query->onLeave(); // employee_status = 'leave'
$query->resigned(); // employee_status = 'resigned'
```
## Accessor
```php
$profile->employee_code; // json_extra['employee_code']
$profile->hire_date; // json_extra['hire_date']
$profile->address; // json_extra['address']
$profile->emergency_contact; // json_extra['emergency_contact']
```
## 연봉 정보 관리 (MNG 내부 API)
> **접근 제한**: 특정 사용자만 접근 가능 (hardcoded: 이의찬, 전진선, 김보곤)
### 엔드포인트
| Method | Path | 설명 |
|--------|------|------|
| GET | `/api/admin/hr/employees/{id}/salary` | 연봉 정보 조회 |
| PUT | `/api/admin/hr/employees/{id}/salary` | 연봉 정보 저장/수정 |
| DELETE | `/api/admin/hr/employees/{id}/salary/history/{historyIndex}` | 연봉 이력 삭제 |
### 데이터 저장 구조
연봉 정보는 `tenant_user_profiles.json_extra.salary_info`에 JSON으로 저장한다.
```json
{
"salary_info": {
"annual_salary": 50000000,
"effective_date": "2026-01-01",
"notes": "연봉 인상",
"history": [
{
"annual_salary": 45000000,
"effective_date": "2025-01-01",
"notes": "입사 시 연봉",
"recorded_at": "2025-01-15 09:00:00",
"recorded_by": "김보곤"
}
]
}
}
```
### 비즈니스 규칙
- R1: 연봉 정보 저장 시 이전 값이 `history` 배열에 자동 추가된다
- R2: 이력 삭제는 배열 인덱스 기반이다 (프론트에서 reverse 표시 → 원본 인덱스 변환 필요)
- R3: 일반 API 응답(`toArray()`)에서 `salary_info`는 자동 제거된다 (민감 데이터 보호)
- R4: 연봉 정보는 MNG 사원관리 페이지의 조회/수정 화면에서만 표시된다
### 전자계약(E-Sign) 연동
근로계약서 사원불러오기에서 연봉 정보를 활용한다:
- `EsignApiController::searchEmployees()``getSalaryInfo()`로 최신 연봉 조회
- `annual_salary` + `salary_effective_date` 반환
- 현재 연봉이 null이면 이력에서 `effective_date` 기준 최신 탐색 (fallback)
- 연봉 적용일 기준으로 연봉계약 시작/종료일 자동 계산
> 상세: [E-Sign 기능 문서 — 근로계약서 사원 연동](../features/esign/README.md#5-근로계약서-사원-연동)
### 관련 파일
| 파일 | 역할 |
|------|------|
| `mng/app/Http/Controllers/Api/Admin/HR/EmployeeSalaryController.php` | 연봉 CRUD API 컨트롤러 |
| `mng/app/Http/Controllers/ESign/EsignApiController.php` | `searchEmployees()` — 전자계약 사원 검색 |
| `mng/app/Models/HR/Employee.php` | `getSalaryInfo()`, `setSalaryInfo()` 메서드 |
| `mng/resources/views/hr/employees/partials/salary-info.blade.php` | 연봉 정보 UI (Alpine.js) |
| `mng/resources/views/esign/create.blade.php` | 근로계약서 변수 자동 매핑 |
| `mng/routes/api.php` | 라우트 정의 (라인 1185-1188) |
---
## 주의사항
1. **주민등록번호**: 반드시 암호화하여 저장
2. **멀티테넌트**: tenant_id 자동 스코핑
3. **Audit**: created_by/updated_by 자동 기록
4. **삭제**: Hard Delete 금지, employee_status 변경으로 처리
5. **연봉 정보**: 특수 권한 사용자만 접근 가능, 일반 API 응답에서 자동 제외