- 완료 문서의 상세 내용은 추후 docs/ 구조화 시 정식 문서에 반영 예정 - HISTORY.md는 요약 인덱스로 유지, 개별 파일은 상세 참조용 보관 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.5 KiB
9.5 KiB
견적 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가지 문제 발생:
- 오른쪽 패널에 제품 리스트가 표시되지 않음
- 개소별 합계(상세소계)가 표시되지 않음
- 상세별 합계(그룹)가 표시되지 않음
- 예상 견적금액이 0원으로 표시됨
1.2 기준 원칙
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 핵심 원칙 │
├─────────────────────────────────────────────────────────────────┤
│ - API 응답 구조와 프론트엔드 기대 구조의 일치 확보 │
│ - Mock 데이터 fallback 로직은 디버깅/테스트용으로만 유지 │
│ - 실제 BOM 계산 결과가 UI에 정확히 반영되도록 수정 │
└─────────────────────────────────────────────────────────────────┘
1.3 변경 승인 정책
| 분류 | 예시 | 승인 |
|---|---|---|
| ✅ 즉시 가능 | 타입 캐스팅 수정, 데이터 매핑 로직 수정 | 불필요 |
| ⚠️ 컨펌 필요 | API 응답 구조 변경, 새 인터페이스 정의 | 필수 |
| 🔴 금지 | API 엔드포인트 변경, DB 스키마 변경 | 별도 협의 |
2. 근본 원인 분석
2.1 API 응답 구조 불일치 (핵심 원인)
API 실제 응답 (actions.ts:962-965):
return {
success: true,
data: result.data || [], // 배열을 직접 반환
};
API 서버 응답 (QuoteCalculationService.php:168-178):
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):
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):
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 수정 (권장)
// actions.ts에서 API 응답 구조 유지
return {
success: true,
data: {
summary: result.data.summary,
items: result.data.items,
},
};
방안 B: QuoteRegistrationV2.tsx 수정
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 스킬로 생성되었습니다.