Files
sam-docs/plans/archive/quote-v2-auto-calculation-fix-plan.md
권혁성 06a4c798ec chore: 완료 계획 문서 22개 archive 이동 및 인덱스 업데이트
- 완료된 계획 문서 22개를 plans/archive/로 이동
  - tracked 16개 (git mv): bending-lot-pipeline, docs-update, fcm-notification 등
  - untracked 6개 (mv): bending-worklog, formula-engine, mng-item 등
- index_plans.md 전면 업데이트
  - 진행중 44개 / 완료 37개 현황 반영
  - 각 문서별 실제 진행률 기재 (0%~94%)
  - 카테고리별 재정리 (견적/생산/품목/문서/마이그레이션/시스템/UI)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:02:47 +09:00

262 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 견적 V2 자동 견적 산출 오류 수정 계획
> **작성일**: 2026-01-26
> **목적**: 자동 견적 산출 기능의 4가지 오류 분석 및 수정
> **기준 문서**: `QuoteRegistrationV2.tsx`, `LocationDetailPanel.tsx`, `QuoteSummaryPanel.tsx`, `actions.ts`
> **상태**: ✅ 완료
---
## 📍 현재 진행 상태
| 항목 | 내용 |
|------|------|
| **마지막 완료 작업** | 테스트 및 검증 완료 |
| **다음 작업** | - |
| **진행률** | 4/4 (100%) ✅ |
| **마지막 업데이트** | 2026-01-26 |
---
## 1. 개요
### 1.1 배경
견적 V2 페이지(`/sales/quote-management/test-new`)에서 자동 견적 산출 버튼 클릭 후 다음 4가지 문제 발생:
1. 오른쪽 패널에 제품 리스트가 표시되지 않음
2. 개소별 합계(상세소계)가 표시되지 않음
3. 상세별 합계(그룹)가 표시되지 않음
4. 예상 견적금액이 0원으로 표시됨
### 1.2 기준 원칙
```
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 핵심 원칙 │
├─────────────────────────────────────────────────────────────────┤
│ - API 응답 구조와 프론트엔드 기대 구조의 일치 확보 │
│ - Mock 데이터 fallback 로직은 디버깅/테스트용으로만 유지 │
│ - 실제 BOM 계산 결과가 UI에 정확히 반영되도록 수정 │
└─────────────────────────────────────────────────────────────────┘
```
### 1.3 변경 승인 정책
| 분류 | 예시 | 승인 |
|------|------|------|
| ✅ 즉시 가능 | 타입 캐스팅 수정, 데이터 매핑 로직 수정 | 불필요 |
| ⚠️ 컨펌 필요 | API 응답 구조 변경, 새 인터페이스 정의 | **필수** |
| 🔴 금지 | API 엔드포인트 변경, DB 스키마 변경 | 별도 협의 |
---
## 2. 근본 원인 분석
### 2.1 API 응답 구조 불일치 (핵심 원인)
**API 실제 응답** (`actions.ts:962-965`):
```typescript
return {
success: true,
data: result.data || [], // 배열을 직접 반환
};
```
**API 서버 응답** (`QuoteCalculationService.php:168-178`):
```php
return [
'success' => $failCount === 0,
'summary' => [
'total_count' => count($inputItems),
'success_count' => $successCount,
'fail_count' => $failCount,
'grand_total' => round($grandTotal, 2),
],
'items' => $results, // items 배열 안에 결과가 있음
];
```
**컴포넌트 기대 구조** (`QuoteRegistrationV2.tsx:459-462`):
```typescript
const apiData = result.data as {
summary?: { grand_total: number };
items?: Array<{ index: number; result: BomCalculationResult }>;
};
const bomItems = apiData.items || []; // ❌ result.data가 배열이면 items가 없음!
```
### 2.2 문제 발생 흐름
```
사용자 → "자동 견적 산출" 클릭
calculateBomBulk(bomItems) 호출
API 서버: { success, summary, items: [...] } 반환
actions.ts: result.data = 전체 응답 객체 (또는 배열로 잘못 파싱)
QuoteRegistrationV2.tsx: result.data.items 접근 시도
❌ items가 undefined → bomItems = []
locations에 bomResult 저장 안됨
LocationDetailPanel: bomResult?.items 없음 → Mock 데이터 표시
QuoteSummaryPanel: bomResult?.subtotals 없음 → Mock 데이터 표시
💥 모든 UI 영역에 데이터 없음
```
### 2.3 영향 받는 컴포넌트
| 컴포넌트 | 파일 | 영향 |
|----------|------|------|
| QuoteRegistrationV2 | `QuoteRegistrationV2.tsx:457-481` | bomResult 저장 안됨 |
| LocationDetailPanel | `LocationDetailPanel.tsx:152-184` | Mock 데이터로 fallback |
| QuoteSummaryPanel | `QuoteSummaryPanel.tsx:136-164` | Mock 데이터로 fallback |
---
## 3. 대상 범위
### 3.1 Phase 1: API 응답 처리 수정
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 1.1 | `actions.ts` 응답 구조 확인 | ✅ | API 서버 응답과 비교 |
| 1.2 | `actions.ts` BomBulkResponse 타입 추가 | ✅ | 정확한 API 응답 구조 정의 |
| 1.3 | `QuoteRegistrationV2.tsx` handleCalculate 수정 | ✅ | 응답 매핑 로직 및 디버그 로그 추가 |
### 3.2 Phase 2: 데이터 바인딩 수정
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 2.1 | `FormulaEvaluatorService.php` items에 process_group 추가 | ✅ | addProcessGroupToItems 메서드 추가 |
| 2.2 | `LocationDetailPanel.tsx` bomItemsByTab 수정 | ✅ | process_group_key 기반 매핑 |
| 2.3 | `QuoteSummaryPanel.tsx` detailTotals 수정 | ✅ | grouped_items에서 items 가져오기 |
| 2.4 | `actions.ts` BomCalculationResult 타입 확장 | ✅ | process_group, grouped_items 필드 추가 |
---
## 4. 상세 작업 내용
### 4.1 Phase 1.2: handleCalculate 함수 수정
**현재 코드** (`QuoteRegistrationV2.tsx:457-479`):
```typescript
if (result.success && result.data) {
// ❌ 잘못된 타입 캐스팅 - result.data 자체가 API 응답 객체임
const apiData = result.data as {
summary?: { grand_total: number };
items?: Array<{ index: number; result: BomCalculationResult }>;
};
const bomItems = apiData.items || []; // ❌ undefined
// ...
}
```
**수정 방안**:
`actions.ts`의 응답 처리를 확인하여 두 가지 접근법 중 선택:
#### 방안 A: actions.ts 수정 (권장)
```typescript
// actions.ts에서 API 응답 구조 유지
return {
success: true,
data: {
summary: result.data.summary,
items: result.data.items,
},
};
```
#### 방안 B: QuoteRegistrationV2.tsx 수정
```typescript
if (result.success && result.data) {
// result.data가 { summary, items } 구조인지 확인
const apiData = result.data as unknown as {
summary?: { grand_total: number };
items?: Array<{ index: number; result: BomCalculationResult }>;
};
// ...
}
```
---
## 5. 컨펌 대기 목록
| # | 항목 | 변경 내용 | 영향 범위 | 상태 |
|---|------|----------|----------|------|
| 1 | API 응답 구조 | actions.ts의 result.data 반환 방식 검토 | react/actions.ts | 대기 |
---
## 6. 변경 이력
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|------|------|----------|------|------|
| 2026-01-26 | 분석 | 문서 초안 작성 및 근본 원인 분석 완료 | - | - |
| 2026-01-26 | 수정 | actions.ts BomBulkResponse 타입 추가 | react/src/components/quotes/actions.ts | ✅ |
| 2026-01-26 | 수정 | QuoteRegistrationV2.tsx handleCalculate 수정 | react/src/components/quotes/QuoteRegistrationV2.tsx | ✅ |
| 2026-01-26 | 수정 | FormulaEvaluatorService.php process_group 추가 | api/app/Services/Quote/FormulaEvaluatorService.php | ✅ |
| 2026-01-26 | 수정 | LocationDetailPanel.tsx bomItemsByTab 수정 | react/src/components/quotes/LocationDetailPanel.tsx | ✅ |
| 2026-01-26 | 수정 | QuoteSummaryPanel.tsx detailTotals 수정 | react/src/components/quotes/QuoteSummaryPanel.tsx | ✅ |
| 2026-01-26 | 수정 | ItemService.php has_bom 필드 추가 | api/app/Services/ItemService.php | ✅ |
| 2026-01-26 | 수정 | actions.ts FinishedGoods에 has_bom, bom 필드 추가 | react/src/components/quotes/actions.ts | ✅ |
| 2026-01-26 | 수정 | QuoteRegistrationV2.tsx DevFill BOM 필터링 | react/src/components/quotes/QuoteRegistrationV2.tsx | ✅ |
| 2026-01-26 | 검증 | 브라우저 테스트 완료 - 4가지 문제 모두 해결 확인 | - | ✅ |
---
## 7. 참고 문서
- **빠른 시작**: `docs/quickstart/quick-start.md`
- **품질 체크리스트**: `docs/standards/quality-checklist.md`
- **API 규칙**: `docs/standards/api-rules.md`
---
## 8. 검증 결과
> 브라우저 자동화 테스트 완료 (2026-01-26)
### 8.1 테스트 케이스
| 입력값 | 예상 결과 | 실제 결과 | 상태 |
|--------|----------|----------|------|
| DevFill 후 자동 견적 산출 | 제품 리스트 표시 | 볼트 M10×40, 너트 M10, 볼트 M8×30 등 6개 품목 표시 | ✅ |
| 개소 선택 | 개소별 합계 표시 | 1F / SS-01 상세소계: 3,119,555.94원 | ✅ |
| 그룹별 합계 | 상세별 합계 표시 | 절곡 공정: 735,891.24원, 철재 공정: 2,383,364.7원 | ✅ |
| 전체 금액 | 예상 견적금액 > 0 | 예상 견적금액: 3,119,555.94원 | ✅ |
### 8.2 테스트 환경
- **URL**: `http://dev.sam.kr/sales/quote-management/test-new`
- **테스트 방법**: Claude-in-Chrome 브라우저 자동화
- **데이터**: DevFill로 생성된 테스트 데이터
### 8.3 추가 발견 및 해결 사항
테스트 중 DevFill이 BOM 없는 제품을 선택하여 계산 결과가 0으로 나오는 문제 발견:
| 문제 | 원인 | 해결 |
|------|------|------|
| DevFill 후 bomItemsCount: 0 | BOM 없는 제품 선택 | DevFill에서 BOM 있는 제품만 필터링 |
| has_bom 필드 없음 | API 응답에 미포함 | ItemService.php에서 계산 필드 추가 |
| getFinishedGoods에서 필드 누락 | 매핑 시 has_bom, bom 미포함 | FinishedGoods 인터페이스 및 매핑 수정 |
### 8.4 최종 검증 결과
```
[DevFill] BOM 있는 제품: 15개 / 전체: 2017개
[BOM 계산 결과]
- bomItemsCount: 6
- bomGrandTotal: 3,119,555.94
- 공정별 그룹: 절곡, 철재
```
**모든 4가지 UI 문제 해결 확인 완료**
---
*이 문서는 /sc:plan 스킬로 생성되었습니다.*