Files
sam-docs/plans/order-management-plan.md

335 lines
12 KiB
Markdown
Raw Normal View History

# 수주관리 (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에서 실제 데이터 조회
- 견적 선택 시 발주처가 자동 설정되고 필드 비활성화
- 로딩 중 "불러오는 중..." 플레이스홀더 표시
---
*이 문서는 독립 세션에서 바로 작업 시작 가능하도록 설계되었습니다.*