docs: 작업 현황 및 관계 문서 업데이트

This commit is contained in:
2025-12-09 09:40:01 +09:00
parent c4a4f85e63
commit b2b3b27f17
2 changed files with 397 additions and 3 deletions

View File

@@ -1,5 +1,388 @@
# SAM API 작업 현황
## 2025-12-09 (월) - HR API 개발 완료 (Employee, Attendance, Department Tree)
### 작업 목표
- `docs/features/HR_API_ANALYSIS.md` 기반 HR API 구현
- Employee 관리 API (tenant_user_profiles 활용)
- Attendance 근태 관리 API (attendances 테이블)
- Department 트리 조회 API
### Phase 1: 마이그레이션 ✅
**추가된 마이그레이션:**
- `2025_12_09_084138_add_employee_status_to_tenant_user_profiles_table.php`
- `employee_status` ENUM('active', 'leave', 'resigned') DEFAULT 'active'
- `json_extra` JSON nullable (유연한 사원 정보)
- `2025_12_09_084231_create_attendances_table.php`
- `user_id`, `base_date`, `status`, `json_details`, `remarks`
- `json_details`: check_in, check_out, gps_data, work_minutes 등
### Phase 2: Employee API ✅
**수정된 파일:**
- `app/Models/Tenants/TenantUserProfile.php` - employee_status, json_extra 헬퍼 추가
**추가된 파일:**
- `app/Services/EmployeeService.php` - 사원 CRUD, 통계, 계정 생성
- `app/Http/Controllers/Api/V1/EmployeeController.php`
- `app/Http/Requests/Employee/` (5개 FormRequest)
- IndexRequest, StoreRequest, UpdateRequest, BulkDeleteRequest, CreateAccountRequest
- `app/Swagger/v1/EmployeeApi.php` - 8개 엔드포인트 문서
**API 엔드포인트 (8개):**
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/v1/employees` | 사원 목록 |
| POST | `/v1/employees` | 사원 등록 |
| GET | `/v1/employees/stats` | 사원 통계 |
| GET | `/v1/employees/{id}` | 사원 상세 |
| PATCH | `/v1/employees/{id}` | 사원 수정 |
| DELETE | `/v1/employees/{id}` | 사원 삭제 |
| POST | `/v1/employees/bulk-delete` | 일괄 삭제 |
| POST | `/v1/employees/{id}/create-account` | 계정 생성 |
### Phase 3: Department Tree API ✅
**수정된 파일:**
- `app/Services/DepartmentService.php` - getTree(), buildTreeNode() 추가
- `app/Http/Controllers/Api/V1/DepartmentController.php` - tree() 액션 추가
- `routes/api.php` - `/v1/departments/tree` 라우트 추가
### Phase 4: Attendance API ✅
**추가된 파일:**
- `app/Models/Tenants/Attendance.php` - 근태 모델
- BelongsToTenant, SoftDeletes
- json_details 헬퍼 (check_in, check_out, gps_data, work_minutes 등)
- 스코프: onDate, betweenDates, forUser, withStatus
- `app/Services/AttendanceService.php` - 근태 CRUD, 출퇴근, 월간 통계
- `app/Http/Controllers/Api/V1/AttendanceController.php`
- `app/Http/Requests/Attendance/` (6개 FormRequest)
- IndexRequest, StoreRequest, UpdateRequest, CheckInRequest, CheckOutRequest, MonthlyStatsRequest
- `app/Swagger/v1/AttendanceApi.php` - 9개 엔드포인트 문서
**API 엔드포인트 (9개):**
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/v1/attendances` | 근태 목록 |
| POST | `/v1/attendances` | 근태 등록 |
| GET | `/v1/attendances/monthly-stats` | 월간 통계 |
| POST | `/v1/attendances/check-in` | 출근 기록 |
| POST | `/v1/attendances/check-out` | 퇴근 기록 |
| GET | `/v1/attendances/{id}` | 근태 상세 |
| PATCH | `/v1/attendances/{id}` | 근태 수정 |
| DELETE | `/v1/attendances/{id}` | 근태 삭제 |
| POST | `/v1/attendances/bulk-delete` | 일괄 삭제 |
### 검증 결과
- ✅ Pint 코드 포맷팅 완료 (13개 파일 수정)
- ✅ 마이그레이션 실행 완료 (Batch 48)
- ✅ 라우트 등록 확인 (Employee 8개, Attendance 9개, Department Tree 1개)
- ✅ Swagger 문서 생성 완료
### 수정된 파일 목록
**routes/api.php:**
- EmployeeController, AttendanceController import 추가
- Employee API 라우트 그룹 (8개)
- Attendance API 라우트 그룹 (9개)
- Department /tree 라우트 추가
**버그 수정:**
- `app/Models/Tenants/Attendance.php` - BelongsToTenant 경로 수정
- `App\Models\Scopes\BelongsToTenant``App\Traits\BelongsToTenant`
### 다음 작업
- [ ] React 프론트엔드 연동
- [ ] 휴가 관리 API 구현 (향후)
---
## 2025-12-08 (일) - Flow Tester Error #61 해결 (GET query 파라미터 처리)
### 문제
- `GET /pricing/cost` 요청 시 422 에러 발생
- 에러: `item_id``item_type_code`가 필수 항목인데 누락
### 원인
- Flow 정의에 `query` 필드로 파라미터 정의되어 있음
- **FlowExecutor**가 `query` 필드를 처리하지 않고 `body`만 처리
- GET 요청은 query string으로 파라미터를 전달해야 하는데 누락됨
### 해결
**수정 파일**: `mng/app/Services/FlowTester/FlowExecutor.php`
```php
// Line 226: query 변수 바인딩 추가
$query = $this->binder->bind($step['query'] ?? []);
// Line 230-234: HTTP 요청에 query 옵션 전달
$response = $this->httpClient->request($method, $endpoint, [
'headers' => $headers,
'body' => $body,
'query' => $query, // 추가
]);
// 결과 로그에도 query 정보 포함
'request' => [
'method' => $method,
'endpoint' => $endpoint,
'headers' => $headers,
'body' => $body,
'query' => $query, // 추가
],
```
### 검증
- PHP 문법 검사: ✅ 통과
---
## 2025-12-08 (일) - Flow Tester Error #60 해결 (중복 테스트 데이터 삭제)
### 문제
- Flow Tester 재실행 시 `error.duplicate_key` 오류 발생
- 이전 테스트 실행(Error #59)이 중간에 실패하면서 테스트 데이터가 남아있음
- 동적 날짜(`{{$date}}`)가 정상 작동 중이지만, 같은 날 재실행 시 중복 발생
### 원인
- Error #59 실행 시 create_price 단계까지 진행 후 실패
- cleanup 단계(delete_price)에 도달하지 못해 테스트 데이터 잔류
- checkDuplicate() 메서드가 기존 데이터 발견
### 해결
```bash
# 잔류 테스트 데이터 삭제
docker exec -i sam-api-1 php artisan tinker --execute="
use App\Models\Products\Price;
Price::where('effective_from', '2025-12-08')->delete();
"
# 결과: 1건 삭제 완료
```
### 추가 권장사항
- Flow Tester 재실행 전 cleanup 또는 setup 스크립트 추가 고려
- 또는 effective_from에 `{{$uuid}}``{{$timestamp}}`를 조합하여 고유성 보장
---
## 2025-12-08 (일) - User 모델 경로 오류 수정 (500 에러 해결)
### 문제
- Flow Tester #59 실행 시 `GET /pricing/{id}/revisions` 에서 500 에러
- 에러: `Class "App\Models\User" not found`
### 원인
- `PriceRevision.php`, `Board.php`에서 잘못된 User 모델 경로 참조
- 실제 경로: `App\Models\Members\User`
### 수정된 파일
- `app/Models/Products/PriceRevision.php` - `\App\Models\User``\App\Models\Members\User`
- `app/Models/Boards/Board.php` - `use App\Models\User``use App\Models\Members\User`
---
## 2025-12-08 (일) - Flow Tester 동적 날짜 변수 적용 (duplicate key 해결)
### 문제
- Flow Tester #58 실행 시 `error.duplicate_key` 에러 발생
- 원인: 하드코딩된 `effective_from: "2025-01-01"`로 인해 이전 테스트 데이터와 중복
### 해결
`{{$date}}` 동적 변수를 사용하여 매번 테스트 시 오늘 날짜 사용
### 수정 내용 (Flow ID: 8 - 단가 관리 CRUD 테스트)
| 스텝 | 필드 | 변경 전 | 변경 후 |
|------|------|---------|---------|
| create_price | effective_from | "2025-01-01" | "{{$date}}" |
| create_price | effective_to | "2025-12-31" | "2099-12-31" |
| create_price_for_finalize | effective_from | "2025-01-01" | "{{$date}}" |
| get_cost | date | "2025-06-15" | "{{$date}}" |
| by_items | date | "2025-06-15" | "{{$date}}" |
### VariableBinder 지원 변수
| 변수 | 설명 | 예시 |
|------|------|------|
| `{{$date}}` | 현재 날짜 (Y-m-d) | 2025-12-08 |
| `{{$datetime}}` | 현재 날짜시간 (Y-m-d H:i:s) | 2025-12-08 14:30:00 |
| `{{$timestamp}}` | Unix 타임스탬프 | 1733637000 |
| `{{$uuid}}` | 랜덤 UUID | 550e8400-e29b-41d4-a716-... |
| `{{$random:N}}` | N자리 랜덤 숫자 | 123456 |
| `{{$faker.xxx}}` | Faker 랜덤 데이터 | 회사명, 이름 등 |
### 해결 원리
- 매번 테스트 시 오늘 날짜가 사용되어 다른 날의 중복 데이터와 충돌 없음
- 플로우 마지막에 `delete_price``cleanup_finalized` 스텝이 테스트 데이터 정리
- 별도의 cleanup 스텝 없이도 반복 실행 가능
---
## 2025-12-08 (일) - Flow Tester HTTP 상태 코드 수정
### 문제
- Flow Tester `POST /pricing` 요청에서 예상 상태 코드 201, 실제 200 반환
- HTTP 표준: POST 리소스 생성 시 201 Created 반환 필요
### 수정 내용
**ApiResponse 클래스 개선:**
- `ApiResponse::success()`: `$statusCode` 파라미터 추가 (기본값 200)
- `ApiResponse::handle()`: 콜백에서 `statusCode` 키로 상태 코드 지정 가능
**PricingController 수정:**
- `store()` 메서드: `'statusCode' => 201` 반환
### 수정된 파일
- `app/Helpers/ApiResponse.php` - 상태 코드 파라미터 추가
- `app/Http/Controllers/Api/V1/PricingController.php` - store 201 반환
### 사용 예시
```php
// Controller에서 201 반환
return ApiResponse::handle(function () use ($request) {
$data = $this->service->store($request->validated());
return ['data' => $data, 'message' => __('message.created'), 'statusCode' => 201];
});
```
### 검증 필요
- [x] Flow Tester 재실행하여 201 응답 확인 ✅
- [x] duplicate key 에러 해결 (동적 날짜 변수 적용) ✅
### 남은 작업 (TODO)
다른 Controller store 메서드 일괄 수정 (27개):
- AdminController, BoardController, CategoryController, CategoryFieldController
- CategoryTemplateController, ClassificationController, ClientGroupController
- CommonController, DesignModelController, EstimateController, FolderController
- ItemsController, MaterialController, MenuController, ModelSetController
- PostController, QuoteController, RoleController, TenantController
- TenantOptionGroupController, TenantOptionValueController, TenantStatFieldController
- ItemMaster 하위: CustomTabController, ItemBomItemController, ItemFieldController
- ItemMaster 하위: ItemSectionController, UnitOptionController
---
## 2025-12-08 (일) - 단가 관리 API 전면 재설계 (prices + price_revisions)
### 작업 목표
- `price_histories``prices` + `price_revisions` 구조로 전면 재설계
- 원가 조회 시 수입검사 입고단가 우선, 표준원가 폴백 로직 구현
- 가격 확정(finalize) 기능으로 불변성 보장
- 리비전 관리로 변경 이력 추적
### 테이블 구조 변경
**prices (단가 마스터):**
| 컬럼 | 설명 |
|------|------|
| item_type_code | 품목유형 (PRODUCT/MATERIAL) |
| item_id | 품목 ID |
| client_group_id | 고객그룹 ID (NULL=기본가) |
| purchase_price | 매입단가 (표준원가) |
| processing_cost | 가공비 |
| loss_rate | LOSS율 (%) |
| margin_rate | 마진율 (%) |
| sales_price | 판매단가 |
| rounding_rule | 반올림 규칙 (round/ceil/floor) |
| rounding_unit | 반올림 단위 (1,10,100,1000) |
| effective_from/to | 적용 기간 |
| status | 상태 (draft/active/inactive/finalized) |
| is_final | 최종 확정 여부 |
**price_revisions (변경 이력):**
| 컬럼 | 설명 |
|------|------|
| price_id | 단가 FK |
| revision_number | 리비전 번호 |
| changed_at | 변경 일시 |
| changed_by | 변경자 ID |
| change_reason | 변경 사유 |
| before_snapshot | 변경 전 JSON |
| after_snapshot | 변경 후 JSON |
### 생성된 파일
**마이그레이션 (4개):**
- `2025_12_08_154633_create_prices_table.php`
- `2025_12_08_154634_create_price_revisions_table.php`
- `2025_12_08_154635_migrate_price_histories_to_prices.php`
- `2025_12_08_154636_drop_price_histories_table.php`
**모델 (2개):**
- `app/Models/Price.php` - BelongsToTenant, SoftDeletes
- `app/Models/PriceRevision.php`
**서비스 (1개):**
- `app/Services/PricingService.php`
- index, show, store, update, destroy (CRUD)
- byItems: 다중 품목 단가 조회
- revisions: 변경 이력 조회
- finalize: 가격 확정 (불변 처리)
- getCost: 원가 조회 (receipt > standard 폴백)
**FormRequest (5개):**
- `app/Http/Requests/Pricing/PriceIndexRequest.php`
- `app/Http/Requests/Pricing/PriceStoreRequest.php`
- `app/Http/Requests/Pricing/PriceUpdateRequest.php`
- `app/Http/Requests/Pricing/PriceByItemsRequest.php`
- `app/Http/Requests/Pricing/PriceCostRequest.php`
**Swagger (1개):**
- `app/Swagger/v1/PricingApi.php` - 전면 재작성
### 삭제된 파일
- `app/Models/PriceHistory.php`
### API 엔드포인트 (9개)
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/pricing` | 단가 목록 (페이지네이션) |
| POST | `/api/v1/pricing` | 단가 생성 |
| GET | `/api/v1/pricing/cost` | 원가 조회 (receipt > standard) |
| POST | `/api/v1/pricing/by-items` | 다중 품목 단가 조회 |
| GET | `/api/v1/pricing/{id}` | 단가 상세 |
| PUT | `/api/v1/pricing/{id}` | 단가 수정 |
| DELETE | `/api/v1/pricing/{id}` | 단가 삭제 |
| POST | `/api/v1/pricing/{id}/finalize` | 가격 확정 |
| GET | `/api/v1/pricing/{id}/revisions` | 변경 이력 조회 |
### 원가 조회 로직 (getCost)
```
1순위: 자재인 경우 → material_receipts.purchase_price_excl_vat
(수입검사 완료된 최신 입고단가)
2순위: prices.purchase_price (표준원가)
총원가 계산:
total_cost = (purchase_price + processing_cost) × (1 + loss_rate/100)
판매가 계산:
sales_price = round(total_cost × (1 + margin_rate/100), rounding_unit, rounding_rule)
```
### 검증 결과
- PHP 문법 검사: ✅ 모든 파일 통과
- Pint 코드 포맷팅: ✅ 14개 파일 수정 완료
- Swagger 문서 생성: ✅ 완료
### 다음 작업
- [ ] 마이그레이션 실행 (php artisan migrate)
- [ ] API 테스트 (Swagger UI)
- [ ] React 프론트엔드 연동
### 참조 문서
- `docs/front/[API-2025-12-08] pricing-api-enhancement-request.md`
- `docs/rules/pricing-policy.md`
---
## 2025-12-04 (수) - 견적 API Phase 3: Controller + FormRequest + Routes + Swagger 완료
### 작업 목표

View File

@@ -1,6 +1,6 @@
# 논리적 데이터베이스 관계 문서
> **자동 생성**: 2025-12-04 20:56:47
> **자동 생성**: 2025-12-08 20:14:33
> **소스**: Eloquent 모델 관계 분석
## 📊 모델별 관계 현황
@@ -8,6 +8,8 @@ ## 📊 모델별 관계 현황
### boards
**모델**: `App\Models\Boards\Board`
- **creator()**: belongsTo → `users`
- **updater()**: belongsTo → `users`
- **customFields()**: hasMany → `board_settings`
- **posts()**: hasMany → `posts`
@@ -299,10 +301,19 @@ ### parts
- **category()**: belongsTo → `common_codes`
- **partType()**: belongsTo → `common_codes`
### price_historys
**모델**: `App\Models\Products\PriceHistory`
### prices
**모델**: `App\Models\Products\Price`
- **clientGroup()**: belongsTo → `client_groups`
- **product()**: belongsTo → `products`
- **material()**: belongsTo → `materials`
- **revisions()**: hasMany → `price_revisions`
### price_revisions
**모델**: `App\Models\Products\PriceRevision`
- **price()**: belongsTo → `prices`
- **changedByUser()**: belongsTo → `users`
### products
**모델**: `App\Models\Products\Product`