fix: FormulaEvaluatorService 슬랫 통합 및 면적/중량 공식 수정

- FormulaEvaluatorService: 슬랫 면적 공식 분리 (W0×(H0+50) vs W1×(H1+550))
- FormulaEvaluatorService: MOTOR_CAPACITY/BRACKET_SIZE 입력값 우선 처리
- KyungdongFormulaHandler: calculateDynamicItems 면적/중량 제품타입별 분기
- KyungdongFormulaHandler: normalizeGuideType() 추가 (벽면↔벽면형 호환)
- KyungdongFormulaHandler: guide_rail_spec 파라미터 별칭 지원
- 검증: 스크린/슬랫 5치수×3수량 전체 5130 정합성 확인 (±1원 이내)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-30 22:39:53 +09:00
parent e9639d1011
commit f4a902fceb
2 changed files with 65 additions and 20 deletions

View File

@@ -1627,27 +1627,45 @@ private function calculateKyungdongBom(
// KyungdongFormulaHandler 인스턴스 생성
$handler = new KyungdongFormulaHandler;
// Step 3: 경동 전용 변수 계산
// Step 3: 경동 전용 변수 계산 (제품타입별 면적/중량 공식)
$W1 = $W0 + 160;
$H1 = $H0 + 350;
$area = ($W1 * ($H1 + 550)) / 1000000;
// 중량 계산 (제품타입별)
if ($productType === 'steel') {
if ($productType === 'slat') {
// 슬랫: W0 × (H0 + 50) / 1M
$area = ($W0 * ($H0 + 50)) / 1000000;
$weight = $area * 25;
$weightFormula = "AREA × 25";
$areaFormula = '(W0 × (H0 + 50)) / 1,000,000';
$areaCalc = "({$W0} × ({$H0} + 50)) / 1,000,000";
$weightFormula = 'AREA × 25';
$weightCalc = "{$area} × 25";
} elseif ($productType === 'steel') {
// 철재: W1 × (H1 + 550) / 1M, 중량 = 면적 × 25
$area = ($W1 * ($H1 + 550)) / 1000000;
$weight = $area * 25;
$areaFormula = '(W1 × (H1 + 550)) / 1,000,000';
$areaCalc = "({$W1} × ({$H1} + 550)) / 1,000,000";
$weightFormula = 'AREA × 25';
$weightCalc = "{$area} × 25";
} else {
// 스크린: W1 × (H1 + 550) / 1M
$area = ($W1 * ($H1 + 550)) / 1000000;
$weight = $area * 2 + ($W0 / 1000) * 14.17;
$weightFormula = "AREA × 2 + (W0 / 1000) × 14.17";
$areaFormula = '(W1 × (H1 + 550)) / 1,000,000';
$areaCalc = "({$W1} × ({$H1} + 550)) / 1,000,000";
$weightFormula = 'AREA × 2 + (W0 / 1000) × 14.17';
$weightCalc = "{$area} × 2 + ({$W0} / 1000) × 14.17";
}
// 모터 용량 결정
$motorCapacity = $handler->calculateMotorCapacity($productType, $weight, $bracketInch);
// 모터 용량 결정 (입력값 우선, 없으면 자동계산)
$motorCapacity = $inputVariables['MOTOR_CAPACITY']
?? $inputVariables['motor_capacity']
?? $handler->calculateMotorCapacity($productType, $weight, $bracketInch);
// 브라켓 크기 결정
$bracketSize = $handler->calculateBracketSize($weight, $bracketInch);
// 브라켓 크기 결정 (입력값 우선, 없으면 자동계산)
$bracketSize = $inputVariables['BRACKET_SIZE']
?? $inputVariables['bracket_size']
?? $handler->calculateBracketSize($weight, $bracketInch);
// 핸들러가 필요한 키 보장 (inputVariables에서 전달되지 않으면 기본값)
$productModel = $inputVariables['product_model'] ?? 'KSS01';
@@ -1692,8 +1710,8 @@ private function calculateKyungdongBom(
[
'var' => 'AREA',
'desc' => '면적',
'formula' => '(W1 × (H1 + 550)) / 1,000,000',
'calculation' => "({$W1} × ({$H1} + 550)) / 1,000,000",
'formula' => $areaFormula,
'calculation' => $areaCalc,
'result' => round($area, 4),
'unit' => '㎡',
],

View File

@@ -392,8 +392,8 @@ public function calculateSteelItems(array $params): array
// 절곡품 관련 파라미터
$caseSpec = $params['case_spec'] ?? '500*380';
$caseLength = (float) ($params['case_length'] ?? ($width + 220)); // mm 단위 (레거시: W0+220)
$guideType = $params['guide_type'] ?? '벽면형'; // 벽면형, 측면형, 혼합형
$guideSpec = $params['guide_spec'] ?? '120*70'; // 120*70, 120*100
$guideType = $this->normalizeGuideType($params['guide_type'] ?? '벽면형');
$guideSpec = $params['guide_spec'] ?? $params['guide_rail_spec'] ?? '120*70';
$guideLength = (float) ($params['guide_length'] ?? ($height + 250)) / 1000; // m 단위 (레거시: H0+250)
$bottomBarLength = (float) ($params['bottombar_length'] ?? $width) / 1000; // m 단위 (레거시: W0)
$lbarLength = (float) ($params['lbar_length'] ?? ($width + 220)) / 1000; // m 단위 (레거시: W0+220)
@@ -669,6 +669,22 @@ private function calculateGuideRails(
return $items;
}
/**
* 가이드타입 정규화 (5130 ↔ SAM 호환)
*
* 5130: '벽면', '측면', '혼합' (col6 필드)
* SAM: '벽면형', '측면형', '혼합형' (switch case)
*/
private function normalizeGuideType(string $type): string
{
return match ($type) {
'벽면', '벽면형' => '벽면형',
'측면', '측면형' => '측면형',
'혼합', '혼합형' => '혼합형',
default => $type,
};
}
// =========================================================================
// 부자재 계산 (3종)
// =========================================================================
@@ -863,11 +879,22 @@ public function calculateDynamicItems(array $inputs): array
$bracketInch = $inputs['bracket_inch'] ?? '5';
$productType = $inputs['product_type'] ?? 'screen';
// 중량 계산 (5130 로직) - W1, H1 기반
$W1 = $width + 160;
$H1 = $height + 350;
$area = ($W1 * ($H1 + 550)) / 1000000;
$weight = $area * ($productType === 'steel' ? 25 : 2) + ($width / 1000) * 14.17;
// 중량 계산 (5130 로직) - 제품타입별 면적/중량 공식
if ($productType === 'slat') {
// 슬랫: W0 × (H0 + 50) / 1M, 중량 = 면적 × 25
$area = ($width * ($height + 50)) / 1000000;
$weight = $area * 25;
} else {
// 스크린/철재: W1 × (H1 + 550) / 1M
$W1 = $width + 160;
$H1 = $height + 350;
$area = ($W1 * ($H1 + 550)) / 1000000;
if ($productType === 'steel') {
$weight = $area * 25;
} else {
$weight = $area * 2 + ($width / 1000) * 14.17;
}
}
// 모터 용량/브라켓 크기 계산 (입력값 우선, 없으면 자동계산)
$motorCapacity = $inputs['MOTOR_CAPACITY'] ?? $this->calculateMotorCapacity($productType, $weight, $bracketInch);
@@ -967,7 +994,7 @@ public function calculateDynamicItems(array $inputs): array
// 4. 절곡품
// installation_type → guide_type 매핑 (calculateSteelItems는 guide_type 사용)
if (isset($inputs['installation_type']) && ! isset($inputs['guide_type'])) {
$inputs['guide_type'] = $inputs['installation_type'];
$inputs['guide_type'] = $this->normalizeGuideType($inputs['installation_type']);
}
$steelItems = $this->calculateSteelItems($inputs);
$items = array_merge($items, $steelItems);