# 견적 수정 → 기존 수주 업데이트 연동 개선 계획 > **작성일**: 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 스킬로 생성되었습니다.*