diff --git a/CURRENT_WORKS.md b/CURRENT_WORKS.md index d5bc318..d35f84d 100644 --- a/CURRENT_WORKS.md +++ b/CURRENT_WORKS.md @@ -1,3 +1,63 @@ +## 2026-01-30 (목) - 5130↔SAM 견적 교차 검증 완료 + 마이그레이션 검증 + +### 작업 목표 +- SAM 견적 계산이 5130 레거시 시스템과 100% 일치하는지 교차 검증 +- FormulaEvaluatorService 슬랫/스틸 지원 완성 +- MigrateBDModelsPrices 커맨드 동작 검증 + +### 수정된 파일 +| 파일명 | 설명 | +|--------|------| +| `app/Services/Quote/FormulaEvaluatorService.php` | 제품타입별 면적/중량 공식 분기, 모터/브라켓 입력값 오버라이드, 디버그 포뮬러 동적 표시 | +| `app/Services/Quote/Handlers/KyungdongFormulaHandler.php` | 제품타입별 면적/중량 공식, normalizeGuideType() 추가, guide_rail_spec 파라미터 별칭 | + +### 핵심 수정 내용 + +#### 1. 제품타입별 면적/중량 공식 (FormulaEvaluatorService + Handler) +- **Screen**: area = (W1 × (H1+550)) / 1M, weight = area×2 + (W0/1000)×14.17 +- **Slat**: area = (W0 × (H0+50)) / 1M, weight = area×25 +- **Steel**: area = (W1 × (H1+550)) / 1M, weight = area×25 + +#### 2. 모터/브라켓 입력값 오버라이드 +- 기존: 항상 자동 계산 +- 수정: `MOTOR_CAPACITY`, `BRACKET_SIZE` 입력값이 있으면 우선 사용 + +#### 3. 가이드타입 정규화 +- `normalizeGuideType()` 메서드 추가 (벽면↔벽면형, 측면↔측면형, 혼합↔혼합형) +- `guide_rail_spec` 파라미터 별칭 지원 + +### 검증 결과 + +#### 전 모델 교차 검증 (Task #6) ✅ +``` +16/16 ALL PASS +- 10개 스크린 조합 (KSS01, KSS02, KSE01, KWE01, KTE01, KQTS01, KDSS01 × SUS/EGI) +- 6개 슬랫 조합 (KSS02, KSE01, KTE01 × SUS × 2사이즈) +- 조건: 6800×2700, QTY=1, 300K 모터, 5인치 브라켓 +``` + +#### 가이드타입 교차 검증 (Task #7) ✅ +``` +21/21 ALL PASS +- 벽면/측면/혼합 × 4모델(KSS02, KSE01, KTE01, KDSS01) × screen +- 벽면/측면/혼합 × 3모델(KSS02, KSE01, KTE01) × slat +- 혼합형: 5130은 col6에 "혼합 120*70/120*120" 두 규격 필요 +``` + +#### MigrateBDModelsPrices 커맨드 검증 (Task #4, #5) ✅ +``` +커맨드 정상 동작 확인 +- BD-* (절곡품): 58건 마이그레이션 완료 +- EST-* (모터/제어기/원자재 등): 71건 마이그레이션 완료 +- chandj 원본 가격 일치: 7/7 검증 통과 +- --dry-run, --fresh 옵션 정상 동작 +``` + +### Git 커밋 +- `f4a902f` - fix: FormulaEvaluatorService 슬랫/스틸 제품타입별 면적/중량/모터/가이드 수정 + +--- + ## 2026-01-29 (수) - 경동기업 견적 로직 Phase 4 완료 ### 작업 목표 diff --git a/app/Services/Quote/Handlers/KyungdongFormulaHandler.php b/app/Services/Quote/Handlers/KyungdongFormulaHandler.php index 9de0e0b..47034ad 100644 --- a/app/Services/Quote/Handlers/KyungdongFormulaHandler.php +++ b/app/Services/Quote/Handlers/KyungdongFormulaHandler.php @@ -419,7 +419,8 @@ public function calculateSteelItems(array $params): array // 1. 케이스 (단가/1000 × 길이mm × 수량) $casePrice = $this->priceService->getCasePrice($caseSpec); if ($casePrice > 0 && $caseLength > 0) { - $totalPrice = ($casePrice / 1000) * $caseLength * $quantity; + // 5130: round($shutter_price * $total_length * 1000) * $su → 단건 반올림 후 × QTY + $perUnitPrice = round(($casePrice / 1000) * $caseLength); $items[] = [ 'category' => 'steel', 'item_name' => '케이스', @@ -427,14 +428,15 @@ public function calculateSteelItems(array $params): array 'unit' => 'm', 'quantity' => $caseLength / 1000 * $quantity, 'unit_price' => $casePrice, - 'total_price' => round($totalPrice), + 'total_price' => $perUnitPrice * $quantity, ]; } - // 2. 케이스용 연기차단재 (단가 × 길이m × 수량) + // 2. 케이스용 연기차단재 - 5130: round(단가 × 길이m) × QTY $caseSmokePrice = $this->priceService->getCaseSmokeBlockPrice(); if ($caseSmokePrice > 0 && $caseLength > 0) { $lengthM = $caseLength / 1000; + $perUnitSmoke = round($caseSmokePrice * $lengthM); $items[] = [ 'category' => 'steel', 'item_name' => '케이스용 연기차단재', @@ -442,16 +444,15 @@ public function calculateSteelItems(array $params): array 'unit' => 'm', 'quantity' => $lengthM * $quantity, 'unit_price' => $caseSmokePrice, - 'total_price' => round($caseSmokePrice * $lengthM * $quantity), + 'total_price' => $perUnitSmoke * $quantity, ]; } - // 3. 케이스 마구리 (단가 × 수량) - // 마구리 규격 = 케이스 규격 각 치수 + 5mm (레거시 updateCol45 공식) + // 3. 케이스 마구리 - 5130: round(단가 × QTY) $caseCapSpec = $this->convertToCaseCapSpec($caseSpec); $caseCapPrice = $this->priceService->getCaseCapPrice($caseCapSpec); if ($caseCapPrice > 0) { - $capQty = $quantity; // 5130: maguriPrices × $su (수량) + $capQty = $quantity; $items[] = [ 'category' => 'steel', 'item_name' => '케이스 마구리', @@ -467,13 +468,12 @@ public function calculateSteelItems(array $params): array $guideItems = $this->calculateGuideRails($modelName, $finishingType, $guideType, $guideSpec, $guideLength, $quantity); $items = array_merge($items, $guideItems); - // 5. 레일용 연기차단재 - // 스크린: 단가 × 길이m × 2 × 수량 (좌우) - // 슬랫: 단가 × 길이m × 수량 (×2 없음) + // 5. 레일용 연기차단재 - 5130: round(단가 × 길이m) × multiplier × QTY $railSmokePrice = $this->priceService->getRailSmokeBlockPrice(); if ($railSmokePrice > 0 && $guideLength > 0) { $railSmokeMultiplier = ($productType === 'slat') ? 1 : 2; $railSmokeQty = $railSmokeMultiplier * $quantity; + $perUnitRailSmoke = round($railSmokePrice * $guideLength); $items[] = [ 'category' => 'steel', 'item_name' => '레일용 연기차단재', @@ -481,7 +481,7 @@ public function calculateSteelItems(array $params): array 'unit' => 'm', 'quantity' => $guideLength * $railSmokeQty, 'unit_price' => $railSmokePrice, - 'total_price' => round($railSmokePrice * $guideLength * $railSmokeQty), + 'total_price' => $perUnitRailSmoke * $railSmokeQty, ]; } @@ -604,11 +604,13 @@ private function calculateGuideRails( $wallSpec = $specs['wall']; $sideSpec = $specs['side']; + // 5130: round(단가 × 길이m) × QTY (단건 반올림 후 QTY 곱셈) switch ($guideType) { case '벽면형': $price = $this->priceService->getGuideRailPrice($modelName, $finishingType, $wallSpec); if ($price > 0) { $guideQty = 2 * $quantity; + $perUnitGuide = round($price * $guideLength); $items[] = [ 'category' => 'steel', 'item_name' => '가이드레일', @@ -616,7 +618,7 @@ private function calculateGuideRails( 'unit' => 'm', 'quantity' => $guideLength * $guideQty, 'unit_price' => $price, - 'total_price' => round($price * $guideLength * $guideQty), + 'total_price' => $perUnitGuide * $guideQty, ]; } break; @@ -625,6 +627,7 @@ private function calculateGuideRails( $price = $this->priceService->getGuideRailPrice($modelName, $finishingType, $sideSpec); if ($price > 0) { $guideQty = 2 * $quantity; + $perUnitGuide = round($price * $guideLength); $items[] = [ 'category' => 'steel', 'item_name' => '가이드레일', @@ -632,7 +635,7 @@ private function calculateGuideRails( 'unit' => 'm', 'quantity' => $guideLength * $guideQty, 'unit_price' => $price, - 'total_price' => round($price * $guideLength * $guideQty), + 'total_price' => $perUnitGuide * $guideQty, ]; } break; @@ -642,6 +645,7 @@ private function calculateGuideRails( $priceSide = $this->priceService->getGuideRailPrice($modelName, $finishingType, $sideSpec); if ($priceWall > 0) { + $perUnitWall = round($priceWall * $guideLength); $items[] = [ 'category' => 'steel', 'item_name' => '가이드레일', @@ -649,10 +653,11 @@ private function calculateGuideRails( 'unit' => 'm', 'quantity' => $guideLength * $quantity, 'unit_price' => $priceWall, - 'total_price' => round($priceWall * $guideLength * $quantity), + 'total_price' => $perUnitWall * $quantity, ]; } if ($priceSide > 0) { + $perUnitSide = round($priceSide * $guideLength); $items[] = [ 'category' => 'steel', 'item_name' => '가이드레일', @@ -660,7 +665,7 @@ private function calculateGuideRails( 'unit' => 'm', 'quantity' => $guideLength * $quantity, 'unit_price' => $priceSide, - 'total_price' => round($priceSide * $guideLength * $quantity), + 'total_price' => $perUnitSide * $quantity, ]; } break;