- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동) - 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/) - 기획팀 폴더 requests/ 생성 - plans/ → dev/dev_plans/ 이름 변경 - README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용) - resources.md 신규 (노션 링크용, assets/brochure 이관 예정) - CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동 - 전체 참조 경로 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
172 lines
6.1 KiB
Markdown
172 lines
6.1 KiB
Markdown
# 견적 수정 → 기존 수주 업데이트 연동 개선 계획
|
|
|
|
> **작성일**: 2026-02-07
|
|
> **목적**: 견적 수정 시 연결된 기존 수주를 자동 업데이트하고, "수주등록" 대신 "수주 보기" 버튼 표시
|
|
> **상태**: 🔄 진행중
|
|
|
|
---
|
|
|
|
## 📍 현재 진행 상태
|
|
|
|
| 항목 | 내용 |
|
|
|------|------|
|
|
| **마지막 완료 작업** | 분석 완료 |
|
|
| **다음 작업** | Phase 1.1 - API 수정 |
|
|
| **진행률** | 0/4 (0%) |
|
|
| **마지막 업데이트** | 2026-02-07 |
|
|
|
|
---
|
|
|
|
## 1. 개요
|
|
|
|
### 1.1 배경
|
|
|
|
사용자가 수주(33) 상세 → 견적(41) 수정 → 저장 후 "수주등록" 버튼이 표시되어 클릭 시 새 수주가 생성됨.
|
|
|
|
**기대 동작**: 견적 수정 → 기존 수주(33)에 자동 반영 + "수주 보기" 버튼으로 기존 수주 이동
|
|
|
|
### 1.2 근본 원인
|
|
|
|
- `Quote.order_id`가 null인 상태에서 `Order.quote_id`만 설정된 경우 발생
|
|
- `QuoteService::update()`는 `quote->order_id` 기준으로만 동기화 트리거
|
|
- `QuoteService::show()`는 역방향 참조(`Order.quote_id`)를 고려하지 않음
|
|
- 결과: 프론트에서 `orderId = null` → "수주등록" 버튼 표시
|
|
|
|
### 1.3 기존 구현 현황 (이미 구현된 부분)
|
|
|
|
| 기능 | 상태 | 위치 |
|
|
|------|------|------|
|
|
| `OrderService::syncFromQuote()` | ✅ 완전 구현 | `api/app/Services/OrderService.php:561-746` |
|
|
| `QuoteService::update()` → syncFromQuote 호출 | ✅ 구현 (order_id 기준) | `api/app/Services/Quote/QuoteService.php:448` |
|
|
| QuoteFooterBar "수주 보기" / "수주등록" 분기 | ✅ 구현 (orderId 기준) | `react/src/components/quotes/QuoteFooterBar.tsx:209` |
|
|
| `transformApiToV2`: order_id → orderId 매핑 | ✅ 구현 | `react/src/components/quotes/types.ts:275,1008` |
|
|
|
|
### 1.4 변경 승인 정책
|
|
|
|
| 분류 | 예시 | 승인 |
|
|
|------|------|------|
|
|
| ✅ 즉시 가능 | 기존 메서드에 조건 추가 | 불필요 |
|
|
| ⚠️ 컨펌 필요 | API 로직 변경, 데이터 보정 | **필수** |
|
|
|
|
---
|
|
|
|
## 2. 대상 범위
|
|
|
|
### 2.1 Phase 1: API 수정 (백엔드)
|
|
|
|
| # | 작업 항목 | 상태 | 비고 |
|
|
|---|----------|:----:|------|
|
|
| 1.1 | QuoteService::show() - 역방향 order 참조 보정 | ⏳ | order_id null이면 orders() 관계로 탐색 |
|
|
| 1.2 | QuoteService::update() - 역방향 sync 트리거 | ⏳ | order_id null이어도 orders() 있으면 동기화 |
|
|
|
|
### 2.2 Phase 2: 프론트엔드 확인
|
|
|
|
| # | 작업 항목 | 상태 | 비고 |
|
|
|---|----------|:----:|------|
|
|
| 2.1 | 견적 수정 후 view 모드 전환 시 데이터 갱신 확인 | ⏳ | orderId가 정상 반영되는지 |
|
|
| 2.2 | "수주 보기" 버튼 동작 확인 | ⏳ | 기존 수주로 정상 이동하는지 |
|
|
|
|
---
|
|
|
|
## 3. 작업 절차
|
|
|
|
### 3.1 상세 변경 사항
|
|
|
|
#### 1.1 QuoteService::show() 수정
|
|
|
|
**파일**: `api/app/Services/Quote/QuoteService.php` (line 168-187)
|
|
|
|
**현재 동작**: Quote 모델을 그대로 반환 (order_id가 null이면 null 그대로)
|
|
|
|
**변경**: quote.order_id가 null인데 Order.quote_id로 연결된 수주가 있으면 order_id를 보정
|
|
|
|
```php
|
|
// show() 메서드 내, return $quote; 직전에 추가
|
|
if (!$quote->order_id) {
|
|
$linkedOrder = \App\Models\Orders\Order::where('quote_id', $quote->id)
|
|
->where('tenant_id', $tenantId)
|
|
->first();
|
|
if ($linkedOrder) {
|
|
// DB에도 반영 (데이터 정합성 복구)
|
|
$quote->update(['order_id' => $linkedOrder->id]);
|
|
$quote->refresh();
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 1.2 QuoteService::update() 동기화 트리거 확장
|
|
|
|
**파일**: `api/app/Services/Quote/QuoteService.php` (line 447-460)
|
|
|
|
**현재 동작**: `if ($quote->order_id)` 일 때만 syncFromQuote 호출
|
|
|
|
**변경**: order_id가 null이어도 orders() 관계로 연결된 수주가 있으면 동기화 실행
|
|
|
|
```php
|
|
// 기존 코드 (line 448)
|
|
if ($quote->order_id) {
|
|
|
|
// 변경 후
|
|
$orderId = $quote->order_id;
|
|
if (!$orderId) {
|
|
// 역방향 참조로 연결된 수주 찾기
|
|
$linkedOrder = \App\Models\Orders\Order::where('quote_id', $quote->id)
|
|
->where('tenant_id', $tenantId)
|
|
->first();
|
|
if ($linkedOrder) {
|
|
$quote->update(['order_id' => $linkedOrder->id]);
|
|
$orderId = $linkedOrder->id;
|
|
}
|
|
}
|
|
if ($orderId) {
|
|
```
|
|
|
|
### 3.2 영향 범위
|
|
|
|
| 영향 받는 부분 | 변경 여부 | 설명 |
|
|
|---------------|----------|------|
|
|
| QuoteService::show() | ✅ 수정 | 역방향 참조 보정 |
|
|
| QuoteService::update() | ✅ 수정 | sync 트리거 확장 |
|
|
| OrderService::syncFromQuote() | ❌ 변경 없음 | 이미 완전 구현 |
|
|
| QuoteFooterBar.tsx | ❌ 변경 없음 | orderId 기준 분기 이미 구현 |
|
|
| QuoteRegistrationV2.tsx | ❌ 변경 없음 | orderId 전달 이미 구현 |
|
|
| types.ts (transformApiToV2) | ❌ 변경 없음 | order_id → orderId 매핑 이미 구현 |
|
|
|
|
---
|
|
|
|
## 4. 검증 방법
|
|
|
|
### 4.1 테스트 시나리오
|
|
|
|
| # | 시나리오 | 예상 결과 |
|
|
|---|---------|----------|
|
|
| 1 | 수주(33) 상세 → 견적(41) 수정 → 저장 | 기존 수주(33) 품목 자동 업데이트 |
|
|
| 2 | 견적(41) 상세 view 모드 진입 | "수주 보기" 버튼 표시 (수주등록 아님) |
|
|
| 3 | "수주 보기" 버튼 클릭 | 수주(33) 상세 페이지로 이동 |
|
|
| 4 | 견적 수정 후 금액 변경 | 수주 총금액도 동기화 |
|
|
|
|
### 4.2 성공 기준
|
|
|
|
- 견적 수정 시 연결된 수주가 자동 업데이트됨
|
|
- "수주 보기" 버튼이 정상 표시됨
|
|
- 기존 수주로 정상 네비게이션됨
|
|
- 기존 convertToOrder() 플로우에 영향 없음
|
|
|
|
---
|
|
|
|
## 5. 참고 파일
|
|
|
|
| 파일 | 역할 |
|
|
|------|------|
|
|
| `api/app/Services/Quote/QuoteService.php` | 견적 서비스 (show, update, convertToOrder) |
|
|
| `api/app/Services/OrderService.php` | 수주 서비스 (syncFromQuote) |
|
|
| `api/app/Models/Quote/Quote.php` | 견적 모델 (orders() 관계) |
|
|
| `api/app/Models/Orders/Order.php` | 수주 모델 (quote() 관계) |
|
|
| `react/src/components/quotes/QuoteFooterBar.tsx` | 견적 푸터 바 (버튼 분기) |
|
|
| `react/src/components/quotes/QuoteRegistrationV2.tsx` | 견적 등록/수정 V2 |
|
|
| `react/src/components/quotes/types.ts` | 타입 및 API→V2 변환 |
|
|
| `react/src/app/[locale]/(protected)/sales/quote-management/[id]/page.tsx` | 견적 상세 페이지 |
|
|
|
|
---
|
|
|
|
*이 문서는 /sc:plan 스킬로 생성되었습니다.* |