# 수주관리 (Order Management) API 연동 계획 > **작성일**: 2025-01-08 > **목적**: 수주관리 페이지 Mock 데이터 → API 연동 > **상태**: ✅ Phase 3 완료 (100% 완료) --- ## 📍 현재 진행 상태 | 항목 | 내용 | |------|------| | **마지막 완료 작업** | 버그 수정 - 목록 페이지 서버 에러 해결 (3건) | | **다음 작업** | 완료 | | **진행률** | 3/3 Phase (100%) + 버그 수정 완료 | | **마지막 업데이트** | 2025-01-09 | | **커밋** | 버그 수정 커밋 완료 | --- ## 1. 개요 ### 1.1 배경 수주관리 페이지는 프론트엔드 UI가 구현되어 있으나, **하드코딩된 Mock 데이터(SAMPLE_ORDERS)**를 사용 중입니다. 실제 비즈니스 운영을 위해 API 연동이 필요합니다. ### 1.2 현재 구현 상태 분석 #### API (Laravel) - ✅ Phase 1 완료 | 구성요소 | 파일 경로 | 상태 | |---------|----------|------| | Model | `api/app/Models/Orders/Order.php` | ✅ 존재 | | Model | `api/app/Models/Orders/OrderItem.php` | ✅ 존재 | | Model | `api/app/Models/Orders/OrderHistory.php` | ✅ 존재 | | Model | `api/app/Models/Orders/OrderVersion.php` | ✅ 존재 | | Model | `api/app/Models/Orders/OrderItemComponent.php` | ✅ 존재 | | Controller | `api/app/Http/Controllers/Api/V1/OrderController.php` | ✅ **완료** | | Service | `api/app/Services/OrderService.php` | ✅ **완료** | | FormRequest | `api/app/Http/Requests/Order/*.php` | ✅ **완료** (3개) | | Route | `/api/v1/orders` | ✅ **완료** (7개 엔드포인트) | | Swagger | `api/app/Swagger/v1/OrderApi.php` | ✅ **완료** | #### Frontend (React/Next.js) - ✅ Phase 2 완료 | 구성요소 | 파일 경로 | 상태 | |---------|----------|------| | 목록 페이지 | `react/src/app/[locale]/(protected)/sales/order-management-sales/page.tsx` | ✅ API 연동 | | 등록 페이지 | `react/src/app/[locale]/(protected)/sales/order-management-sales/new/page.tsx` | ✅ API 연동 | | 상세 페이지 | `react/src/app/[locale]/(protected)/sales/order-management-sales/[id]/page.tsx` | ✅ API 연동 | | 수정 페이지 | `react/src/app/[locale]/(protected)/sales/order-management-sales/[id]/edit/page.tsx` | ✅ API 연동 | | 생산지시 페이지 | `react/src/app/[locale]/(protected)/sales/order-management-sales/[id]/production-order/page.tsx` | ✅ 완료 | | 등록 컴포넌트 | `react/src/components/orders/OrderRegistration.tsx` | ✅ 완료 | | 견적선택 다이얼로그 | `react/src/components/orders/QuotationSelectDialog.tsx` | ✅ 완료 | | 품목추가 다이얼로그 | `react/src/components/orders/ItemAddDialog.tsx` | ✅ 완료 | | **actions.ts** | `react/src/components/orders/actions.ts` | ✅ **완료** | ### 1.3 연관관계 ``` ┌─────────────────┐ ┌─────────────────┐ │ Quote │────── quote_id ────▶│ Order │ │ (견적서) │ │ (수주) │ └─────────────────┘ └─────────────────┘ │ │ sales_order_id ▼ ┌─────────────────┐ │ WorkOrder │ │ (작업지시) │ └─────────────────┘ ``` ### 1.4 변경 승인 정책 | 분류 | 예시 | 승인 | |------|------|------| | ✅ 즉시 가능 | 필드 추가/변경, API 엔드포인트 추가 | 불필요 | | ⚠️ 컨펌 필요 | 테이블 구조 변경, 기존 API 수정 | **필수** | | 🔴 금지 | 기존 Order 모델 구조 변경 | 별도 협의 | --- ## 2. 대상 범위 ### Phase 1: API 개발 (✅ 완료) | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1.1 | OrderController 생성 | ✅ | CRUD + 상태관리 (7개 메서드) | | 1.2 | OrderService 생성 | ✅ | 비즈니스 로직 (index, stats, show, store, update, destroy, updateStatus) | | 1.3 | FormRequest 생성 | ✅ | Store, Update, UpdateStatus (3개) | | 1.4 | API 라우트 등록 | ✅ | routes/api.php (7개 엔드포인트) | | 1.5 | Swagger 문서 작성 | ✅ | OrderApi.php (스키마 8개) | ### Phase 2: Frontend 연동 (✅ 완료) | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 2.1 | actions.ts 생성 | ✅ | API 호출 함수 + 타입 정의 + 변환 함수 | | 2.2 | 목록 페이지 연동 | ✅ | getOrders(), getOrderStats() 연동 | | 2.3 | 상세 페이지 연동 | ✅ | getOrderById() 연동 + 타입 오류 수정 | | 2.4 | 등록 페이지 연동 | ✅ | createOrder() 연동 | | 2.5 | 수정 페이지 연동 | ✅ | updateOrder() 연동 + 타입 오류 수정 | ### Phase 3: 고급 기능 (✅ 완료) | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 3.1 | 견적서 → 수주 변환 | ✅ | QuotationSelectDialog + createOrderFromQuote() | | 3.2 | 생산지시 생성 연동 | ✅ | createProductionOrder() + production-order 페이지 | | 3.3 | 상태 흐름 관리 | ✅ | 수주확정 다이얼로그 + updateOrderStatus() | --- ## 3. API 엔드포인트 설계 ### 3.1 REST API | Method | Endpoint | 설명 | 우선순위 | |--------|----------|------|:--------:| | GET | `/api/v1/orders` | 수주 목록 조회 (페이징/필터) | 🔴 | | GET | `/api/v1/orders/stats` | 수주 통계 | 🔴 | | GET | `/api/v1/orders/{id}` | 수주 상세 조회 | 🔴 | | POST | `/api/v1/orders` | 수주 생성 | 🔴 | | PUT | `/api/v1/orders/{id}` | 수주 수정 | 🟡 | | DELETE | `/api/v1/orders/{id}` | 수주 삭제 | 🟡 | | PATCH | `/api/v1/orders/{id}/status` | 상태 변경 | 🟡 | | POST | `/api/v1/orders/{id}/production-order` | 생산지시 생성 | 🟢 | | POST | `/api/v1/orders/from-quote/{quoteId}` | 견적→수주 변환 | 🟢 | ### 3.2 데이터 스키마 #### Order (수주) - 기존 모델 기반 ```typescript interface Order { id: number; tenantId: number; quoteId?: number; // 원본 견적 orderNo: string; // 수주번호 (KD-TS-YYMMDD-NN) orderTypeCode: 'ORDER' | 'PURCHASE'; statusCode: 'DRAFT' | 'CONFIRMED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED'; clientId?: number; clientName?: string; siteName?: string; // 현장명 quantity: number; supplyAmount: number; taxAmount: number; totalAmount: number; deliveryDate?: Date; deliveryMethodCode?: string; memo?: string; createdBy?: number; updatedBy?: number; createdAt: Date; updatedAt: Date; // Relations items?: OrderItem[]; client?: Client; } ``` #### OrderItem (수주 품목) ```typescript interface OrderItem { id: number; orderId: number; itemId?: number; itemName: string; specification?: string; quantity: number; unit?: string; unitPrice: number; supplyAmount: number; taxAmount: number; totalAmount: number; sortOrder: number; } ``` --- ## 4. 작업 절차 ### Step 1: API 개발 (Backend) ``` 1. OrderService 생성 ├── index(): 목록 조회 (페이징, 필터링) ├── show(): 상세 조회 ├── store(): 생성 ├── update(): 수정 ├── destroy(): 삭제 ├── updateStatus(): 상태 변경 ├── stats(): 통계 조회 └── createFromQuote(): 견적→수주 변환 2. OrderController 생성 ├── FormRequest DI └── ApiResponse::handle() 사용 3. FormRequest 생성 ├── StoreOrderRequest └── UpdateOrderRequest 4. 라우트 등록 └── Route::prefix('orders')->group(...) 5. Swagger 문서 작성 └── app/Swagger/v1/OrderApi.php ``` ### Step 2: Frontend 연동 ``` 1. actions.ts 생성 ├── getOrders(): 목록 조회 ├── getOrderById(): 상세 조회 ├── createOrder(): 생성 ├── updateOrder(): 수정 ├── deleteOrder(): 삭제 ├── updateOrderStatus(): 상태 변경 └── getOrderStats(): 통계 조회 2. 페이지별 연동 ├── page.tsx: SAMPLE_ORDERS → getOrders() ├── [id]/page.tsx: Mock → getOrderById() ├── new/page.tsx: Mock → createOrder() └── [id]/edit/page.tsx: Mock → updateOrder() ``` --- ## 5. 의존성 ### 5.1 필수 선행 작업 - **없음** - Order 모델 이미 존재, 바로 작업 가능 ### 5.2 연관 기능 (선택적) - **견적관리 (Quote)**: 견적→수주 변환 시 필요 - **거래처관리 (Client)**: 거래처 연동 - **품목관리 (Item)**: 품목 마스터 연동 ### 5.3 후속 연동 - **작업지시 (WorkOrder)**: 생산지시 생성 시 `sales_order_id` 연결 - **출하관리**: 수주 완료 후 출하 처리 --- ## 6. 참고 문서 - **빠른 시작**: `docs/quickstart/quick-start.md` - **API 규칙**: `docs/standards/api-rules.md` - **품질 체크리스트**: `docs/standards/quality-checklist.md` - **DB 스키마**: `docs/specs/database-schema.md` - **Swagger 가이드**: `docs/guides/swagger-guide.md` ### 참고 코드 - **작업지시 API (참고용)**: `api/app/Http/Controllers/Api/V1/WorkOrderController.php` - **공정관리 actions.ts (참고용)**: `react/src/components/process-management/actions.ts` --- ## 7. 검증 방법 ### 7.1 API 테스트 ```bash # 목록 조회 curl -X GET "http://api.sam.kr/api/v1/orders" -H "X-Api-Key: ..." # 상세 조회 curl -X GET "http://api.sam.kr/api/v1/orders/1" -H "X-Api-Key: ..." # 통계 조회 curl -X GET "http://api.sam.kr/api/v1/orders/stats" -H "X-Api-Key: ..." ``` ### 7.2 성공 기준 | 기준 | 측정 방법 | |------|----------| | API CRUD 동작 | Swagger UI 테스트 통과 | | 목록 페이지 | 실제 데이터 표시 | | 상세 페이지 | 수주 정보 정상 표시 | | 등록/수정 | 데이터 저장 및 조회 | | 상태 변경 | DRAFT → CONFIRMED 전환 | --- ## 8. 자기완결성 점검 | # | 검증 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1 | 작업 목적이 명확한가? | ✅ | Mock → API 연동 | | 2 | 성공 기준이 정의되어 있는가? | ✅ | 섹션 7 참조 | | 3 | 작업 범위가 구체적인가? | ✅ | Phase 1-3 단계별 정의 | | 4 | 의존성이 명시되어 있는가? | ✅ | 선행 작업 없음 | | 5 | 참고 파일 경로가 정확한가? | ✅ | 모든 경로 검증됨 | | 6 | 단계별 절차가 실행 가능한가? | ✅ | Step 1-2 상세 정의 | | 7 | 검증 방법이 명시되어 있는가? | ✅ | curl 테스트 + 기준 | | 8 | 모호한 표현이 없는가? | ✅ | 구체적 파일/엔드포인트 명시 | --- ## 9. 버그 수정 이력 ### 2025-01-09: 목록 페이지 서버 에러 수정 | # | 파일 | 문제 | 수정 내용 | |---|------|------|----------| | 1 | `react/.../page.tsx:120` | API 응답 데이터 구조 불일치 | `ordersResult.data` → `ordersResult.data.items` | | 2 | `api/.../OrderService.php:113` | Quote 필드명 오류 | `quote:id,quote_no,site_name` → `quote:id,quote_number,site_name` | | 3 | `react/.../actions.ts:384` | Quote 필드명 오류 | `apiData.quote?.quote_no` → `apiData.quote?.quote_number` | **원인 분석:** - `getOrders()` 함수는 `{ items: Order[], total, page, totalPages }` 구조를 반환하나, 페이지에서 `ordersResult.data`를 직접 사용하여 타입 불일치 발생 - Quote 모델의 필드명이 `quote_number`인데 `quote_no`로 잘못 참조 **영향 범위:** - 수주 목록 페이지 접근 시 서버 에러 발생 - 견적 연동 수주의 견적번호 표시 오류 ### 2025-01-09: 수주 등록 페이지 거래처 API 연동 | # | 파일 | 변경 내용 | |---|------|----------| | 1 | `react/.../OrderRegistration.tsx` | `SAMPLE_CLIENTS` 하드코딩 제거 | | 2 | `react/.../OrderRegistration.tsx` | `useClientList` 훅으로 실제 API 연동 | | 3 | `react/.../OrderRegistration.tsx` | 로딩 상태 처리 ("불러오는 중...") | | 4 | `react/.../OrderRegistration.tsx` | 견적 선택 시 발주처 필드 비활성화 | **개선 내용:** - 발주처(거래처) 드롭다운이 `/api/proxy/clients` API에서 실제 데이터 조회 - 견적 선택 시 발주처가 자동 설정되고 필드 비활성화 - 로딩 중 "불러오는 중..." 플레이스홀더 표시 --- *이 문서는 독립 세션에서 바로 작업 시작 가능하도록 설계되었습니다.*