# 경동기업 견적 로직 분석 및 구현 계획

> **작성일**: 2026-01-28
> **목적**: 5130 레거시 견적 시스템 분석 → SAM 동적 BOM/견적 로직 구현
> **선행 작업**: [kd-items-migration-plan.md](./kd-items-migration-plan.md) (정적 품목/단가 완료)
> **상태**: 🔄 Phase 0 진행중

---

## 🚀 Quick Start

### 이 문서의 목적
정적 품목 데이터는 이관 완료 (items 651건, prices 651건). 이제 **동적으로 BOM을 계산하고 견적을 산출하는 로직**을 5130에서 분석하여 SAM에 구현.

### 환경 정보
| 항목 | 값 |
|------|-----|
| 레거시 소스 | `5130/` (프로젝트 루트) |
| 대상 테넌트 | 287 (경동기업) |
| 관련 SAM 페이지 | https://dev.sam.kr/sales/quote-management/new |

---

## 📍 현재 진행 상태

| 항목 | 내용 |
|------|------|
| **현재 단계** | Phase 4: SAM 구현 완료 ✅ |
| **다음 작업** | Phase 5: 통합 테스트 및 프론트엔드 연동 |
| **진행률** | 4/5 (100%) - Phase 0~4 완료 |
| **마지막 업데이트** | 2026-01-29 |

### Phase 4.8 테스트 결과 (2026-01-29)
```
📊 테스트 입력값
- W0: 3000mm, H0: 2500mm, QTY: 1
- 철재형, 5인치 브라켓, 매립형 제어기
- KSS01 모델, SUS 마감

📦 계산된 항목 (16개)
1. 주자재(스크린) → 228,750원
2. 모터 400K → 150,000원
3. 제어기 매립형 → 45,000원
4. 케이스 → 45,000원
5. 케이스용 연기차단재 → 10,500원
6. 케이스 마구리 → 10,000원
7. 가이드레일 → 73,200원
8. 레일용 연기차단재 → 15,250원
9. 하장바 → 24,000원
10. L바 → 13,500원
11. 보강평철 → 9,000원
12. 무게평철12T → 24,000원
13. 환봉 → 8,000원
14. 감기샤프트 5인치 → 65,000원
15. 각파이프 → 12,000원
16. 앵글 앵글3T → 18,000원

💰 합계: 751,200원 ✅
```

---

## 1. 배경 및 문제 정의

### 1.1 현재 상황

**SAM 견적 화면에서 FG-KSS01-벽면형-SUS 선택 시:**
- 현재: 3개 항목만 표시 (가이드레일, 하단마감재, L-BAR)
- 기대: 본체, 절곡품, 모터/제어기, 부자재 등 전체 BOM

### 1.2 레거시 DB 구조 (분석 완료)

```
models (모델 마스터)
  └─ parts (대분류 부품: 가이드레일, 하단마감재)
       └─ parts_sub (세부 절곡품: 1번마감제, 2번본체, 3번-C, 4번-D...)
```

**KSS01 벽면형 예시:**
| 대분류 (parts) | 세부품 (parts_sub) | 재질 | 수량 |
|---------------|-------------------|------|------|
| 가이드레일 | 1번(마감제) | SUS 1.2T | 1 |
| | 2번(본체) | EGI 1.55T | 2 |
| | 3번(벽면형-C) | EGI 1.55T | 1 |
| | 4번(벽면형-D) | EGI 1.55T | 1 |
| 하단마감재 | 1번(하장바) | SUS 1.5T | 1 |

### 1.3 동적 항목 (5130 분석 필요)

| 항목 | 설명 | 레거시 소스 |
|------|------|-------------|
| 모터 | W0, H0 기반 용량 자동 계산 | 5130 로직 분석 필요 |
| 제어기 | 모터 사양에 따라 연동 | 5130 로직 분석 필요 |
| 부자재 | 모델/규격별 자동 추가 | 5130 로직 분석 필요 |
| 절곡품 수량 | 파라미터 기반 동적 계산 | 5130 로직 분석 필요 |

---

## 2. 분석 대상

### 2.1 5130 디렉토리 구조 (분석 완료)

```
5130/
├── estimate/              # 견적 관련 (핵심 분석 대상)
│   ├── README.md          # 시스템 문서
│   ├── estimate.php       # 스크린 견적 메인 페이지
│   ├── slat.php           # 철재 견적 메인 페이지
│   ├── get_screen_amount.php  # 스크린 금액 계산 엔진 ⭐
│   ├── get_slat_amount.php    # 철재 금액 계산 엔진 ⭐
│   ├── fetch_unitprice.php    # 단가 조회 유틸리티 ⭐
│   ├── write_form.php         # 견적서 양식 생성
│   └── common/
│       └── calculation.js     # 프론트엔드 계산 로직
├── output/                # 출력/리포트
├── dbeditor/              # DB 관리
└── [기타 모듈]/
```

### 2.2 분석 우선순위

| 순위 | 대상 | 목적 |
|------|------|------|
| 1 | 견적 생성 로직 | BOM 자동 구성 방식 파악 |
| 2 | 모터 계산 로직 | W0/H0 → 모터 용량 공식 |
| 3 | 절곡품 계산 로직 | 파라미터 → 수량/단가 공식 |
| 4 | 부자재 추가 로직 | 모델별 자동 추가 규칙 |
| 5 | 가격 산출 로직 | 최종 견적 금액 계산 |

---

## 3. 작업 계획

### Phase 0: 5130 탐색 및 구조 파악 ✅
- [x] 5130/ 디렉토리 구조 분석
- [x] 견적 관련 파일 식별 (estimate/, output/)
- [x] 주요 함수/클래스 목록화 (아래 섹션 4.3 참조)

### Phase 1: 견적 생성 로직 분석 🔄
- [x] 모델 선택 → BOM 구성 흐름 파악
- [x] 동적 항목 추가 조건 분석 (체크박스 기반)
- [x] DB 조회 패턴 파악 (BDmodels, price_* 테이블)
- [ ] 세부 계산 로직 문서화

### Phase 2: 계산 공식 추출 ✅
- [x] 모터 용량 계산 공식 (`calculateMotorSpec` 분석 완료)
- [x] 절곡품 수량/단가 계산 공식 (섹션 4.12 참조)
- [x] 부자재 자동 추가 규칙 (섹션 4.13 참조)

### Phase 3: SAM 설계 ✅
- [x] 기존 견적 시스템 분석 (QuoteCalculationService, FormulaEvaluatorService)
- [x] 5130 로직 통합 설계 → 하이브리드 접근 결정 (섹션 10.1)
- [x] API 엔드포인트 확장 설계 → 기존 엔드포인트 활용
- [x] DB 스키마 변경 필요 여부 → kd_price_tables 신규 테이블 (옵션)

### Phase 4: SAM 구현 🔄
- [x] 4.1 KyungdongFormulaHandler 클래스 생성 (경동 전용) ✅
- [x] 4.2 FormulaEvaluatorService 확장 (tenant 분기) ✅
- [x] 4.3 모터 용량 계산 구현 ✅
- [x] 4.4 kd_price_tables 마이그레이션 + Model 생성 ✅
- [x] 4.5 price_* 테이블 조회 로직 구현 (KdPriceTable 연동) ✅
- [x] 4.6 단가 데이터 마이그레이션 (Seeder) ✅
- [ ] 4.7 절곡품 계산 구현 (10종)
- [ ] 4.8 API 테스트 및 검증

### Phase 5: 검증
- [ ] 레거시 vs SAM 결과 비교
- [ ] 사용자 테스트
- [ ] 배포

---

## 4. 레거시 분석 기록

### 4.1 분석된 테이블

| 테이블 | 용도 | 분석 상태 |
|--------|------|-----------|
| models | 모델 마스터 | ✅ 완료 |
| parts | 대분류 부품 | ✅ 완료 |
| parts_sub | 세부 절곡품 | ✅ 완료 |
| BDmodels | BOM + 단가 JSON | ✅ 완료 |
| price_motor | 모터 단가 | ✅ 완료 |
| price_shaft | 샤프트 계산 참조 | ✅ 완료 |
| price_pipe | 파이프 계산 참조 | ✅ 완료 |
| price_raw_materials | 원자재 단가 | ✅ 완료 |

### 4.2 분석된 5130 코드

| 파일/모듈 | 내용 | 분석 상태 |
|-----------|------|-----------|
| estimate/README.md | 시스템 문서 | ✅ 완료 |
| estimate/estimate.php | 스크린 견적 메인 | ✅ 완료 |
| estimate/get_screen_amount.php | 스크린 금액 계산 엔진 | ✅ 완료 |
| estimate/get_slat_amount.php | 철재 금액 계산 엔진 | ✅ 완료 |
| estimate/fetch_unitprice.php | 단가 조회 유틸리티 | ✅ 완료 |
| estimate/common/calculation.js | 프론트엔드 계산 | ✅ 완료 |

### 4.3 핵심 함수 목록

#### 금액 계산 함수
| 함수명 | 파일 | 역할 |
|--------|------|------|
| `calculateScreenAmount()` | get_screen_amount.php | 스크린 견적 총액 계산 |
| `calculateSlatAmount()` | get_slat_amount.php | 철재 견적 총액 계산 |
| `calculateGuideRailPrice()` | get_screen_amount.php | 가이드레일 단가 계산 |
| `calculateShaftPrice()` | get_screen_amount.php | 감기샤프트 단가 계산 |

#### 단가 조회 함수 (fetch_unitprice.php)
| 함수명 | 역할 | 참조 테이블 |
|--------|------|-------------|
| `searchBracketSize()` | 모터 중량 → 브라켓 크기 | - |
| `calculateMotorSpec()` | 중량/인치 → 모터 용량 (150K~1000K) | - |
| `getPriceForMotor()` | 모터 용량 → 단가 조회 | price_motor |
| `calculateControllerSpec()` | 제어기 타입 → 단가 조회 | price_motor |
| `calculatePipe()` | 파이프 규격 → 단가 조회 | price_pipe |
| `calculateShaft()` | 샤프트 규격 → 단가 조회 | price_shaft |
| `calculateAngle()` | 앵글 규격 → 단가 조회 | price_angle |
| `slatPrice()` | 원자재 → 단가 조회 | price_raw_materials |

### 4.4 모터 용량 계산 공식 (추출 완료)

```
모터 용량 = f(제품타입, 중량, 브라켓인치)

┌──────────┬─────────┬──────────────────────────────────┐
│ 제품타입  │ 인치    │ 중량 범위 → 용량                  │
├──────────┼─────────┼──────────────────────────────────┤
│ 스크린    │ 4"      │ ≤150kg → 150K                    │
│          │         │ 150~300kg → 300K                 │
│          │         │ 300~400kg → 400K                 │
├──────────┼─────────┼──────────────────────────────────┤
│ 스크린    │ 5"      │ ≤123kg → 150K                    │
│          │         │ 123~246kg → 300K                 │
│          │         │ 246~327kg → 400K                 │
│          │         │ 327~500kg → 500K                 │
│          │         │ 500~600kg → 600K                 │
├──────────┼─────────┼──────────────────────────────────┤
│ 스크린    │ 6"      │ ≤104kg → 150K                    │
│          │         │ 104~208kg → 300K                 │
│          │         │ 208~300kg → 400K                 │
│          │         │ 300~424kg → 500K                 │
│          │         │ 424~508kg → 600K                 │
├──────────┼─────────┼──────────────────────────────────┤
│ 철재      │ 4"      │ ≤300kg → 300K                    │
│          │         │ 300~400kg → 400K                 │
├──────────┼─────────┼──────────────────────────────────┤
│ 철재      │ 5"      │ ≤246kg → 300K                    │
│          │         │ 246~327kg → 400K                 │
│          │         │ 327~500kg → 500K                 │
│          │         │ 500~600kg → 600K                 │
├──────────┼─────────┼──────────────────────────────────┤
│ 철재      │ 6"      │ ≤208kg → 300K                    │
│          │         │ 208~277kg → 400K                 │
│          │         │ 277~424kg → 500K                 │
│          │         │ 424~508kg → 600K                 │
│          │         │ 508~800kg → 800K                 │
│          │         │ 800~1000kg → 1000K               │
├──────────┼─────────┼──────────────────────────────────┤
│ 철재      │ 8"      │ ≤324kg → 500K                    │
│          │         │ 324~388kg → 600K                 │
│          │         │ 388~611kg → 800K                 │
│          │         │ 611~1000kg → 1000K               │
└──────────┴─────────┴──────────────────────────────────┘
```

### 4.5 견적 항목 구성 (스크린)

체크박스 옵션에 따라 동적으로 항목 포함/제외:

| 체크박스 | 포함 항목 |
|---------|----------|
| `slatcheck` (주자재) | 주자재(스크린), 환봉 |
| `steel` (절곡) | 케이스, 가이드레일, 하장바, L바, 보강평철, 연기차단재(케이스/레일), 케이스 마구리 |
| `motor` (모터) | 모터 (경동견적가포함일 때만) |
| `partscheck` (부자재) | 감기샤프트, 무게평철12T, 각파이프, 앵글 |
| `warranty` (보증) | (금액 조정에 영향) |

### 4.6 가격 산출 흐름

```
1. 검사비 (고정)
2. 주자재 = 원자재단가 × 면적(W×H/1000000)
3. 모터 = 용량별 단가표 조회
4. 제어기 = 매립/노출/뒷박스 × 수량
5. 케이스 = 규격별 단가 × 길이(m)
6. 가이드레일 = 모델|마감재|규격별 단가 × 길이(m) × 2
7. 하장바/L바 = 단가 × 길이(m)
8. 샤프트 = 규격별(3",4",5") × 길이별(3000~8200) 단가표
9. 파이프 = 두께(1.4) × 길이(3000/6000) 단가표
10. 앵글 = 타입(3T/4T) × 두께(2.5) × 수량
```

---

## 5. 기술적 고려사항

### 5.1 SAM 아키텍처 준수

```php
// Service-First 패턴
class QuoteBomService extends Service
{
    public function calculateDynamicBom(int $modelId, array $parameters): array
    {
        // 1. 정적 BOM 조회 (items.bom)
        // 2. 파라미터 기반 동적 항목 계산
        // 3. 모터/제어기 자동 추가
        // 4. 부자재 자동 추가
        // 5. 단가 계산
    }
}
```

### 5.2 API 설계 (예상)

```
POST /api/v1/quotes/calculate-bom
Request:
{
    "model_id": 13147,  // FG-KSS01-벽면형-SUS
    "parameters": {
        "W0": 3000,     // 폭
        "H0": 2000,     // 높이
        "installation_type": "벽면형",
        "power_source": "220V"
    }
}

Response:
{
    "static_bom": [...],      // 기존 items.bom
    "dynamic_items": [...],   // 모터, 제어기, 부자재
    "calculated_values": {
        "motor_capacity": "150K",
        "total_area": 6.0,
        "estimated_weight": 45.5
    },
    "pricing": {...}
}
```

---

## 6. 관련 문서

- [kd-items-migration-plan.md](./kd-items-migration-plan.md) - 정적 품목/단가 이관 (완료)
- [kd-orders-migration-plan.md](./kd-orders-migration-plan.md) - 입고/재고/주문 이관
- SAM API Rules - api/CLAUDE.md

---

## 7. 변경 이력

| 날짜 | 항목 | 변경 내용 |
|------|------|----------|
| 2026-01-28 | 문서 생성 | 초기 계획 수립, 레거시 DB 분석 결과 반영 |
| 2026-01-28 | Phase 0 완료 | 5130 estimate 디렉토리 분석 완료, 핵심 함수 목록화 |
| 2026-01-28 | Phase 1-2 진행 | 모터 용량 계산 공식 추출, 가격 산출 흐름 문서화 |
| 2026-01-28 | Phase 2 계속 | 브라켓 크기 공식, BDmodels 구조, SAM 매핑 전략 추가 |
| 2026-01-28 | Phase 3 시작 | 기존 SAM 견적 시스템 분석, 5130 통합 설계 문서화 |
| 2026-01-29 | 설계 결정 | 체크박스 방식 → "전체계산 → 개별제거" 방식으로 변경 |
| 2026-01-29 | Phase 2 완료 | 절곡품/부자재/주자재 계산 공식 추출 완료 (4.12~4.15) |
| 2026-01-29 | Phase 4 설계 | 하이브리드 접근 결정 (범용 + 경동전용 Handler), 구현 계획 수립 |
| 2026-01-29 | Phase 4.1 완료 | KyungdongFormulaHandler 기본 구조 생성 (모터/브라켓 계산) |
| 2026-01-29 | Phase 4.2 완료 | FormulaEvaluatorService 확장 (tenant_id=287 분기 처리) |
| 2026-01-29 | Phase 4.3~4.5 완료 | kd_price_tables 마이그레이션, KdPriceTable 모델, Seeder, 단가 조회 연동 |
| 2026-01-29 | Phase 4.6 완료 | 부자재 계산 (3종: 샤프트, 파이프, 앵글) 구현 |

---

### 4.7 브라켓 크기 결정 공식 (추출 완료)

```
searchBracketSize(중량, 인치) → 브라켓크기

┌──────────┬──────────────────────────────────┐
│ 모터용량  │ 브라켓 사이즈                     │
├──────────┼──────────────────────────────────┤
│ 300K     │ 530*320                          │
│ 400K     │ 530*320                          │
│ 500K     │ 600*350                          │
│ 600K     │ 600*350                          │
│ 800K     │ 690*390                          │
│ 1000K    │ 690*390                          │
└──────────┴──────────────────────────────────┘

[중량만으로 판단 (인치 없을 때)]
- ≤300kg → 300K
- ≤400kg → 400K
- ≤500kg → 500K
- ≤600kg → 600K
- ≤800kg → 800K
- ≤1000kg → 1000K
```

### 4.8 견적 입력 컬럼 매핑 (스크린)

| 컬럼 | 필드명 | 설명 |
|------|--------|------|
| col4 | 모델코드 | KSS01, KWS01 등 |
| col5 | 제목 | 현장명 |
| col6 | 가이드레일타입 | 벽면형, 측면형, 혼합형 |
| col7 | 마감재질 | SUS, EGI 등 |
| col10 | 폭(W) | mm 단위 |
| col11 | 높이(H) | mm 단위 |
| col14 | 수량 | 대수 |
| col15~17 | 제어기 | 매립형/노출형/뒷박스 수량 |
| col18_brand | 모터업체 | 경동(견적가포함) 등 |
| col19 | 모터용량 | 150K~1000K |
| col22 | 앵글사이즈 | 모터받침용 |
| col23 | 가이드레일길이 | mm 단위 |
| col31~35 | 연기차단재 | 각 규격별 수량 |
| col36 | 케이스규격 | 또는 col36_custom |
| col37 | 케이스길이 | mm 단위 |
| col45 | 마구리규격 | |
| col48 | 하장바길이 | mm 단위 |
| col49~50 | 하장바 | 수량 관련 |
| col51 | L바길이 | mm 단위 |
| col52~53 | L바 | 수량 관련 |
| col54 | 보강평철길이 | mm 단위 |
| col55~56 | 보강평철 | 수량 관련 |
| col57 | 무게평철 | 수량 |
| col59~65 | 샤프트 | 규격별(3"/4"/5") × 길이별 |
| col68~69 | 각파이프 | 3000/6000 수량 |
| col70 | 환봉 | 수량 |
| col71 | 앵글 | 수량 |

### 4.9 단가 테이블 JSON 구조

**price_shaft (샤프트)**
- col4: 사이즈 (3, 4, 5인치)
- col10: 길이 (m 단위, 예: 3.0 = 3000mm)
- col19: 판매가

**price_pipe (각파이프)**
- col2: 길이 (3000, 6000)
- col4: 두께 (1.4)
- col8: 판매가

**price_angle (앵글)**
- col2: 타입 (스크린용, 철재용)
- col3: 브라켓크기 (530*320, 600*350, 690*390)
- col4: 앵글타입 (앵글3T, 앵글4T)
- col10: 두께 (2.5)
- col19: 판매가

**price_motor (모터/제어기)**
- col2: 용량/타입 (150K, 300K, 매립형, 노출형, 뒷박스)
- col13: 판매가

**price_raw_materials (원자재)**
- col2: 원자재명 (스크린, 슬랫, 조인트바 등)
- col13: 판매가

### 4.10 BDmodels 테이블 구조 (절곡품 단가)

**컬럼 구조:**
| 컬럼 | 설명 | 예시 |
|------|------|------|
| model_name | 모델코드 | KSS01, KWS01, KDSS01 |
| seconditem | 부품분류 | 케이스, 가이드레일, 하단마감재, L-BAR |
| spec | 규격 | 120*70, 650*550 |
| finishing_type | 마감재질 | SUS, EGI |
| unitprice | 단가 | 원/m 또는 원/개 |

**seconditem 종류:**
- 케이스: 케이스박스 (규격별 단가)
- 가이드레일: 레일 (모델+마감+규격별 단가)
- 하단마감재: 하장바 (모델+마감별 단가)
- L-BAR: L바 (모델별 단가)
- 보강평철: 평철 (공통 단가)
- 마구리: 케이스 마감재 (규격별 단가)
- 케이스용 연기차단재: (공통 단가)
- 가이드레일용 연기차단재: (공통 단가)

**단가 조회 키 패턴:**
```php
// 가이드레일: 모델코드|마감재질|규격
$key = "KSS01|SUS|120*70";
$price = $guidrailPrices[$key];

// 케이스: 규격만
$price = $shutterBoxprices["650*550"];

// 하단마감재: 모델코드 + 마감재질 매칭
if ($prodcode == $modelCode && $finishing == $load_finishingType) {
    $price = $bottomBarPrices;
}
```

### 4.11 SAM 매핑 전략

**현재 SAM items 테이블의 BOM 구조:**
```json
// items.bom (JSON)
[
  {"child_item_id": 123, "quantity": 1},  // 가이드레일
  {"child_item_id": 456, "quantity": 1},  // 하단마감재
  {"child_item_id": 789, "quantity": 1}   // L-BAR
]
```

**문제점:**
- 정적 BOM만 저장 (동적 계산 불가)
- 모터/제어기/부자재 누락
- 파라미터(W, H, 수량) 기반 수량 계산 없음

**해결 방안 (Phase 3 설계 시 반영):**
```php
// 1. 정적 BOM + 동적 계산 분리
class QuoteBomService {
    public function calculate(int $modelId, array $params): array
    {
        // 1. 정적 BOM 조회 (items.bom)
        $staticBom = $this->getStaticBom($modelId);

        // 2. 동적 항목 계산
        $dynamicItems = $this->calculateDynamicItems($params);

        // 3. 단가 적용
        return $this->applyPricing($staticBom, $dynamicItems, $params);
    }

    private function calculateDynamicItems(array $params): array
    {
        $items = [];

        // 모터 (체크박스 옵션)
        if ($params['motor_check']) {
            $motorCapacity = $this->calculateMotorCapacity(
                $params['weight'],
                $params['bracket_inch']
            );
            $items['motor'] = $this->getMotorPrice($motorCapacity);
        }

        // 제어기
        $items['controller'] = $this->calculateController($params);

        // 샤프트/파이프/앵글 (부자재)
        if ($params['parts_check']) {
            $items['shaft'] = $this->calculateShaft($params);
            $items['pipe'] = $this->calculatePipe($params);
            $items['angle'] = $this->calculateAngle($params);
        }

        return $items;
    }
}
```

### 4.12 절곡품 계산 공식 (steel 체크박스)

**절곡품 = BDmodels 테이블 조회 (seconditem별 단가)**

| 품목 | 조회 키 | 계산식 | 비고 |
|------|--------|--------|------|
| 케이스 | `seconditem='케이스', spec=규격` | 단가/1000 × 길이(mm) × 수량 | 기본단가 500*380 기준 면적비 계산 |
| 케이스용 연기차단재 | `seconditem='케이스용 연기차단재'` | 단가 × 길이(m) × 수량 | |
| 케이스 마구리 | `seconditem='마구리', spec=규격` | 단가 × 수량 | col45 규격 |
| 가이드레일 | `model_name\|finishing_type\|spec` | 단가 × 길이(m) × 수량 | 벽면/측면 ×2, 혼합 각1 |
| 레일용 연기차단재 | `seconditem='가이드레일용 연기차단재'` | 단가 × 길이(m) × 2 × 수량 | |
| 하장바 | `model_name, seconditem='하단마감재', finishing_type` | 단가 × 길이(m) × 수량 | col48 길이 |
| L바 | `model_name, seconditem='L-BAR'` | 단가 × 길이(m) × 수량 | col51 길이 |
| 보강평철 | `seconditem='보강평철'` | 단가 × 길이(m) × 수량 | col54 길이 |
| 무게평철12T | 고정 12,000원 | 12,000 × col57(수량) | |
| 환봉 | 고정 2,000원 | 2,000 × col70(수량) | |

**가이드레일 타입별 처리:**
```
벽면형(120*70)  → baseKey|120*70 × 2개
측면형(120*100) → baseKey|120*100 × 2개
혼합형(120*70+120*100) → baseKey|120*70 + baseKey|120*100 (각 1개)
```

### 4.13 부자재 계산 공식 (partscheck 체크박스)

**부자재 = price_* 테이블 조회 (JSON itemList)**

| 품목 | 테이블 | 조회 조건 | 계산식 |
|------|--------|----------|--------|
| 감기샤프트 | price_shaft | col4=사이즈, col10=길이(m) | col19(판매가) × 수량 |
| 각파이프 | price_pipe | col4=두께(1.4), col2=길이 | col8(판매가) × 수량 |
| 앵글 | price_angle | col2='앵글3T', col10=두께(2.5) | col19(판매가) × 수량 |

**샤프트 규격별 컬럼 매핑:**
```
col59 → 3" × 300mm (사실상 미사용)
col60 → 4" × 3000mm
col61 → 4" × 4500mm
col62 → 4" × 6000mm
col63 → 5" × 6000mm
col64 → 5" × 7000mm
col65 → 5" × 8200mm
```

**각파이프 컬럼 매핑:**
```
col68 → 1.4T × 3000mm 수량
col69 → 1.4T × 6000mm 수량
```

### 4.14 모터 받침용 앵글 (특수 조건)

```
조건: col22(앵글사이즈) 값이 있고,
      (slatcheck만 체크 AND motor/steel/partscheck 모두 미체크) 가 아닐 때

계산: calculateAngle(수량, itemList, '스크린용') × 수량 × 4
```

### 4.15 주자재 계산 공식 (slatcheck 체크박스)

```
스크린 가격 = 원자재단가 × 면적(㎡)

면적 = W × (H + 550) / 1,000,000
      ↑ 550mm는 스크린 기본 여유분 (350 + 200 추가)

원자재단가: price_raw_materials 테이블에서 col2='실리카' 조회 → col13(판매가)
```

---

## 8. 다음 작업 (TODO)

### 즉시 필요
1. ~~**브라켓 크기 결정 공식**~~ ✅ 완료
2. ~~**BDmodels 조회 패턴**~~ ✅ 완료
3. **절곡품 수량 계산 공식** - parts_sub 기반 동적 수량 결정 로직 (선택적)

### SAM 구현 시 고려사항
1. **전체 계산 → 개별 제거 방식**: 5130의 체크박스 방식 대신, 전체 BOM 계산 후 불필요 항목 제거
2. **단가 테이블 통합**: price_motor, price_shaft, price_pipe 등 → SAM prices 테이블과 연동
3. **BOM 동적 생성 API**: 파라미터(W0, H0) 입력 → 전체 견적 항목 반환 → UI에서 개별 제거

---

## 9. Phase 3: SAM 설계

### 9.1 기존 SAM 견적 시스템 분석

#### 현재 구조
```
api/app/Services/Quote/
├── QuoteCalculationService.php   # 견적 계산 메인 서비스
├── FormulaEvaluatorService.php   # 수식 평가 엔진
├── QuoteService.php              # 견적 CRUD
└── Requests/
    └── QuoteBomCalculateRequest.php  # BOM 계산 입력값 검증
```

#### QuoteCalculationService 핵심 메서드
| 메서드 | 역할 | 입력 | 출력 |
|--------|------|------|------|
| `calculate()` | 일반 견적 계산 | inputs, productCategory, productId | items, costs, errors |
| `calculateBom()` | BOM 기반 견적 | finishedGoodsCode, inputs, debug | finished_goods, items, grand_total |
| `calculateBomBulk()` | 다건 BOM 계산 | inputItems[], debug | summary, items[] |
| `preview()` | 견적 미리보기 | inputs, productCategory, productId | (calculate와 동일) |
| `recalculate()` | 기존 견적 재계산 | Quote | (calculate와 동일) |

#### FormulaEvaluatorService 10단계 BOM 계산
```
Step 1: 입력값 수집 (W0, H0, QTY, PC, GT, MP, CT, WS, INSP)
Step 2: 완제품 선택 (finishedGoodsCode → Item 조회)
Step 3: 변수 계산 (수식 기반 중간값)
Step 4: BOM 전개 (items.bom JSON → 구성품 목록)
Step 5: 단가 출처 결정 (prices 테이블 or 수식 계산)
Step 6: 수량 수식 평가 (formula → 실제 수량)
Step 7: 단가 계산 (unit_price × quantity)
Step 8: 공정별 그룹화 (category 기준)
Step 9: 소계 계산 (그룹별 합계)
Step 10: 최종 합계 (grand_total)
```

#### 현재 입력 파라미터 (QuoteBomCalculateRequest)
| 파라미터 | 설명 | 필수 |
|---------|------|------|
| W0 | 개구부 폭(mm) | ✅ |
| H0 | 개구부 높이(mm) | ✅ |
| QTY | 수량 | ✅ |
| PC | 제품코드 | ✅ |
| GT | 가이드타입 | ❌ |
| MP | 모터파워 | ❌ |
| CT | 제어타입 | ❌ |
| WS | 와이어사이드 | ❌ |
| INSP | 검사비 | ❌ |

### 9.2 5130 로직 통합 설계

#### 5130 vs SAM 비교
| 항목 | 5130 | SAM (현재) | SAM (목표) |
|------|------|-----------|-----------|
| 모터 계산 | `calculateMotorSpec()` | 없음 (수동 입력) | 자동 계산 |
| 브라켓 크기 | `searchBracketSize()` | 없음 | 자동 계산 |
| 항목 선택 | 체크박스 (사전 선택) | 없음 | **전체계산 → 개별제거** |
| 절곡품 단가 | BDmodels 테이블 | prices 테이블 | prices + 범위 조회 |
| 부자재 | 파라미터 기반 동적 추가 | 정적 BOM | 동적 계산 |

#### 항목 선택 방식 변경 (중요)

**5130 방식 (체크박스):**
```
☑ 주자재  ☑ 절곡  ☐ 모터  ☑ 부자재  →  계산
```

**SAM 방식 (전체계산 → 개별제거):**
```
전체 BOM 계산 → 견적 라인 표시 → 불필요 항목 제거/수량 조정

┌─────────────────────────────────────────────────┐
│ 품목명          │ 수량 │ 단가   │ 금액    │ ⊘  │
├─────────────────────────────────────────────────┤
│ 스크린원단      │  6㎡ │ 15,000 │  90,000 │    │
│ 가이드레일      │  4m  │ 12,000 │  48,000 │    │
│ 모터 300K       │  1   │ 85,000 │  85,000 │ ✕  │ ← 제거 가능
│ 제어기 매립형    │  1   │ 25,000 │  25,000 │ ✕  │ ← 제거 가능
│ 감기샤프트 4"   │  1   │ 35,000 │  35,000 │    │
└─────────────────────────────────────────────────┘
```

**장점:**
- 더 직관적인 UX (전체를 보고 판단)
- 개별 품목 단위 제어 가능 (카테고리 단위보다 유연)
- SAM 기존 견적 라인 구조와 호환

#### 확장 필요 항목

**1. 입력 파라미터 추가**
```php
// QuoteBomCalculateRequest 확장
'bracket_inch' => 'nullable|string|in:4,5,6,8',  // 브라켓 인치
'estimated_weight' => 'nullable|numeric',         // 예상 중량
'guide_rail_type' => 'nullable|string',           // 가이드레일 타입
'finishing_type' => 'nullable|string',            // 마감재질
// 체크박스 옵션은 제거 (전체 계산 후 개별 제거 방식)
```

**2. FormulaEvaluatorService 확장**
```php
// 5130 계산 함수 추가
private function calculateMotorCapacity(string $productType, float $weight, string $bracketInch): string
{
    // 4.4 모터 용량 계산 공식 구현
}

private function calculateBracketSize(float $weight, ?string $bracketInch = null): string
{
    // 4.7 브라켓 크기 결정 공식 구현
}

private function calculateDynamicItems(array $params): array
{
    // 체크박스 옵션에 따른 동적 항목 생성
}
```

**3. 단가 조회 확장**
```php
// 범위 기반 단가 조회 (price_shaft, price_pipe 등)
private function getPriceByRange(string $priceTable, array $conditions): ?float
{
    // JSON 데이터에서 범위 조건으로 단가 조회
}
```

### 9.3 API 엔드포인트 설계

#### 기존 엔드포인트 (유지)
```
POST /api/v1/quotes/calculate-bom
POST /api/v1/quotes/calculate-bom-bulk
GET  /api/v1/quotes/input-schema
```

#### 신규 엔드포인트 (추가)
```
POST /api/v1/quotes/calculate-motor
  - 입력: weight, bracket_inch, product_type
  - 출력: motor_capacity, bracket_size

POST /api/v1/quotes/calculate-dynamic-items
  - 입력: model_id, W0, H0, options (체크박스)
  - 출력: dynamic_items[] (모터, 제어기, 부자재)

GET  /api/v1/quotes/price-tables/{table}
  - 테이블: motor, shaft, pipe, angle, raw_materials
  - 출력: 단가표 데이터 (프론트엔드 참조용)
```

### 9.4 DB 스키마 변경 (최소화)

**변경 불필요:**
- items, prices 테이블: 기존 구조 활용
- quote_formulas: 기존 수식 시스템 활용

**검토 필요:**
- `items.metadata` JSON 필드에 5130 특수 정보 저장 가능
- `quote_formula_ranges`: 범위 기반 단가 조회에 활용

**대안:**
- 5130 price_* 테이블 데이터를 `quote_formula_ranges`로 마이그레이션
- 또는 별도 `kd_price_tables` 테이블 생성 (tenant_id=287 전용)

### 9.5 구현 우선순위

| 순위 | 항목 | 난이도 | 의존성 |
|------|------|--------|--------|
| 1 | 모터 용량 계산 함수 | 낮음 | 없음 |
| 2 | 브라켓 크기 계산 함수 | 낮음 | 없음 |
| 3 | 체크박스 옵션 → 동적 항목 | 중간 | 1, 2 |
| 4 | 범위 기반 단가 조회 | 중간 | 없음 |
| 5 | API 엔드포인트 추가 | 낮음 | 1-4 |
| 6 | 프론트엔드 연동 | 중간 | 5 |

---

## 10. Phase 4: 구현 상세 계획

### 10.1 아키텍처 결정: 하이브리드 접근

**배경:**
- 5130 경동 로직은 3차원 조건, 외부 테이블 조회 등 복잡
- 현재 SAM quote_formulas 시스템으로 표현 불가
- 범용으로 만들면 다른 테넌트에 불필요한 복잡성

**결정:**
```
[범용 레이어] - quote_formulas 테이블
├── 단순 계산, 1차원 범위, 단순 매핑
└── 기본 테넌트들이 사용

[테넌트 전용 레이어] - 전용 Handler 클래스
├── tenant_id = 287 (경동기업)
│   └── KyungdongFormulaHandler.php
└── tenant_id = 기타 → 기본 수식 시스템
```

### 10.2 파일 구조

```
api/app/Services/Quote/
├── QuoteCalculationService.php      # 기존 (수정)
├── FormulaEvaluatorService.php      # 기존 (확장)
└── Handlers/
    └── KyungdongFormulaHandler.php  # 신규 (경동 전용)

api/database/seeders/Kyungdong/
├── KyungdongItemSeeder.php          # 기존 (품목/단가)
└── KyungdongPriceTableSeeder.php    # 신규 (price_* 데이터)
```

### 10.3 KyungdongFormulaHandler 설계

```php
namespace App\Services\Quote\Handlers;

class KyungdongFormulaHandler
{
    // 모터 용량 계산 (3차원 조건)
    public function calculateMotorCapacity(
        string $productType,  // screen, steel
        float $weight,
        string $bracketInch   // 4, 5, 6, 8
    ): string;  // 150K, 300K, ...

    // 브라켓 크기 결정
    public function calculateBracketSize(
        float $weight,
        ?string $bracketInch = null
    ): string;  // 530*320, 600*350, 690*390

    // 절곡품 계산 (10종)
    public function calculateSteelItems(array $params): array;

    // 부자재 계산 (3종)
    public function calculatePartItems(array $params): array;

    // 주자재 계산 (스크린)
    public function calculateScreenPrice(
        float $width,
        float $height
    ): float;

    // BDmodels 단가 조회
    private function getBDModelPrice(
        string $modelName,
        string $secondItem,
        ?string $finishingType = null,
        ?string $spec = null
    ): float;

    // price_* 테이블 조회
    private function getPriceFromTable(
        string $tableName,
        array $conditions
    ): float;
}
```

### 10.4 FormulaEvaluatorService 확장

```php
// FormulaEvaluatorService.php

public function calculateBomWithDebug(
    string $finishedGoodsCode,
    array $inputs,
    int $tenantId
): array {
    // 테넌트별 분기
    if ($tenantId === 287) {
        return $this->calculateKyungdongBom($finishedGoodsCode, $inputs);
    }

    // 기본 로직 (기존 코드)
    return $this->calculateDefaultBom($finishedGoodsCode, $inputs);
}

private function calculateKyungdongBom(
    string $finishedGoodsCode,
    array $inputs
): array {
    $handler = new KyungdongFormulaHandler();

    // 1. 기본 BOM 전개 (items.bom)
    $staticBom = $this->getStaticBom($finishedGoodsCode);

    // 2. 동적 항목 계산
    $dynamicItems = $handler->calculateDynamicItems($inputs);

    // 3. 전체 항목 병합
    return $this->mergeAndCalculatePrices($staticBom, $dynamicItems, $inputs);
}
```

### 10.5 단가 데이터 마이그레이션

**옵션 A: 기존 prices 테이블 활용**
- items에 품목 추가, prices에 단가 추가
- 장점: 기존 구조 활용
- 단점: 복잡한 조회 조건 표현 어려움

**옵션 B: 전용 테이블 생성 (권장)**
```sql
-- 경동 전용 단가 테이블
CREATE TABLE kd_price_tables (
    id BIGINT PRIMARY KEY,
    tenant_id BIGINT DEFAULT 287,
    table_name VARCHAR(50),      -- motor, shaft, pipe, angle, bdmodels
    item_data JSON,              -- 원본 JSON 데이터
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);
```

### 10.6 구현 순서

| 순서 | 작업 | 상태 |
|------|------|------|
| 1 | KyungdongFormulaHandler 기본 구조 | ✅ 완료 |
| 2 | 모터/브라켓 계산 메서드 | ✅ 완료 |
| 3 | kd_price_tables 마이그레이션 | ✅ 완료 |
| 4 | KdPriceTable 모델 생성 | ✅ 완료 |
| 5 | KdPriceTableSeeder 생성 | ✅ 완료 |
| 6 | 단가 조회 메서드 (KdPriceTable 연동) | ✅ 완료 |
| 7 | 부자재 계산 (3종) | ✅ 완료 |
| 8 | 절곡품 계산 (10종) | ⏳ 대기 |
| 9 | FormulaEvaluatorService 연동 | ✅ 완료 |
| 10 | API 테스트 및 검증 | ⏳ 대기 |
| 8 | API 테스트 | |

---

*이 문서는 5130 분석 진행에 따라 지속 업데이트됩니다.*