where('tenant_id', $tenantId) ->pluck('id', 'code') ->toArray(); if (empty($categoryMap)) { $this->command->error('QuoteFormulaSeeder: 카테고리가 없습니다. QuoteFormulaCategorySeeder를 먼저 실행하세요.'); return; } $formulas = [ // ============================== // 1. 오픈사이즈 (OPEN_SIZE) - 2개 // ============================== [ 'category_code' => 'OPEN_SIZE', 'variable' => 'W0', 'name' => '오픈사이즈 W0 (가로)', 'type' => 'input', 'formula' => null, 'description' => '자동 견적 산출 섹션의 오픈사이즈 W0 입력값', 'metadata' => ['unit' => 'mm'], 'sort_order' => 1, ], [ 'category_code' => 'OPEN_SIZE', 'variable' => 'H0', 'name' => '오픈사이즈 H0 (세로)', 'type' => 'input', 'formula' => null, 'description' => '자동 견적 산출 섹션의 오픈사이즈 H0 입력값', 'metadata' => ['unit' => 'mm'], 'sort_order' => 2, ], // ============================== // 2. 제작사이즈 (MAKE_SIZE) - 4개 // ============================== [ 'category_code' => 'MAKE_SIZE', 'variable' => 'W1_SCREEN', 'name' => '제작사이즈 W1 (스크린)', 'type' => 'calculation', 'formula' => 'W0 + 140', 'description' => '스크린 제작 가로 = 오픈 가로 + 140', 'metadata' => ['unit' => 'mm', 'product_type' => 'screen'], 'sort_order' => 1, ], [ 'category_code' => 'MAKE_SIZE', 'variable' => 'H1_SCREEN', 'name' => '제작사이즈 H1 (스크린)', 'type' => 'calculation', 'formula' => 'H0 + 350', 'description' => '스크린 제작 세로 = 오픈 세로 + 350', 'metadata' => ['unit' => 'mm', 'product_type' => 'screen'], 'sort_order' => 2, ], [ 'category_code' => 'MAKE_SIZE', 'variable' => 'W1_STEEL', 'name' => '제작사이즈 W1 (철재)', 'type' => 'calculation', 'formula' => 'W0 + 110', 'description' => '철재 제작 가로 = 오픈 가로 + 110', 'metadata' => ['unit' => 'mm', 'product_type' => 'steel'], 'sort_order' => 3, ], [ 'category_code' => 'MAKE_SIZE', 'variable' => 'H1_STEEL', 'name' => '제작사이즈 H1 (철재)', 'type' => 'calculation', 'formula' => 'H0 + 350', 'description' => '철재 제작 세로 = 오픈 세로 + 350', 'metadata' => ['unit' => 'mm', 'product_type' => 'steel'], 'sort_order' => 4, ], // ============================== // 3. 면적 (AREA) - 1개 // ============================== [ 'category_code' => 'AREA', 'variable' => 'M', 'name' => '면적 계산', 'type' => 'calculation', 'formula' => 'W1 * H1 / 1000000', 'description' => '면적(㎡) = 제작가로(W1) × 제작세로(H1) ÷ 1,000,000', 'metadata' => ['unit' => '㎡'], 'sort_order' => 1, ], // ============================== // 4. 중량 (WEIGHT) - 2개 // ============================== [ 'category_code' => 'WEIGHT', 'variable' => 'K_SCREEN', 'name' => '중량 계산 (스크린)', 'type' => 'calculation', 'formula' => 'M * 2 + W0 / 1000 * 14.17', 'description' => '스크린 중량(kg) = 면적 × 2 + (오픈가로 ÷ 1000 × 14.17)', 'metadata' => ['unit' => 'kg', 'product_type' => 'screen'], 'sort_order' => 1, ], [ 'category_code' => 'WEIGHT', 'variable' => 'K_STEEL', 'name' => '중량 계산 (철재)', 'type' => 'calculation', 'formula' => 'M * 25', 'description' => '철재 중량(kg) = 면적 × 25', 'metadata' => ['unit' => 'kg', 'product_type' => 'steel'], 'sort_order' => 2, ], // ============================== // 5. 가이드레일 (GUIDE_RAIL) - 5개 // ============================== [ 'category_code' => 'GUIDE_RAIL', 'variable' => 'G', 'name' => '가이드레일 제작길이', 'type' => 'calculation', 'formula' => 'H0 + 250', 'description' => '가이드레일 제작길이(G) = 오픈세로(H0) + 250', 'metadata' => ['unit' => 'mm'], 'sort_order' => 1, ], [ 'category_code' => 'GUIDE_RAIL', 'variable' => 'GR_AUTO_SELECT', 'name' => '가이드레일 자재 자동 선택', 'type' => 'range', 'formula' => null, 'description' => '가이드레일 길이 및 수량 자동 산출 (기본 2개)', 'metadata' => ['unit' => 'EA', 'input_variable' => 'G'], 'sort_order' => 2, 'ranges' => [ ['min' => 1219, 'max' => 2438, 'result' => '2438 2개', 'quantity' => 2, 'description' => '1219 < G ≤ 2438'], ['min' => 2438, 'max' => 3000, 'result' => '3000 2개', 'quantity' => 2, 'description' => '2438 < G ≤ 3000'], ], ], [ 'category_code' => 'GUIDE_RAIL', 'variable' => 'GR_QTY_WALL', 'name' => '가이드레일 수량 (벽면형)', 'type' => 'calculation', 'formula' => 'BASE_QTY', 'description' => '벽면형: 기본 수량 그대로 (좌우 2개)', 'metadata' => ['unit' => 'EA', 'install_type' => 'wall'], 'sort_order' => 3, 'is_active' => false, // 미정의 변수(BASE_QTY) 사용 ], [ 'category_code' => 'GUIDE_RAIL', 'variable' => 'GR_QTY_SIDE', 'name' => '가이드레일 수량 (측면형)', 'type' => 'calculation', 'formula' => 'BASE_QTY / 2', 'description' => '측면형: 기본 수량의 절반 (한쪽만)', 'metadata' => ['unit' => 'EA', 'install_type' => 'side'], 'sort_order' => 4, 'is_active' => false, // 미정의 변수(BASE_QTY) 사용 ], [ 'category_code' => 'GUIDE_RAIL', 'variable' => 'GR_QTY_MIXED', 'name' => '가이드레일 수량 (혼합형)', 'type' => 'calculation', 'formula' => 'BASE_QTY * 1.5', 'description' => '혼합형: 기본 수량 × 1.5', 'metadata' => ['unit' => 'EA', 'install_type' => 'mixed'], 'sort_order' => 5, 'is_active' => false, // 미정의 변수(BASE_QTY) 사용 ], // ============================== // 6. 케이스 (CASE) - 3개 // ============================== [ 'category_code' => 'CASE', 'variable' => 'S_SCREEN', 'name' => '케이스 사이즈 (스크린)', 'type' => 'calculation', 'formula' => 'W0 + 220', 'description' => '스크린 케이스 사이즈(S) = 오픈가로(W0) + 220', 'metadata' => ['unit' => 'mm', 'product_type' => 'screen'], 'sort_order' => 1, ], [ 'category_code' => 'CASE', 'variable' => 'S_STEEL', 'name' => '케이스 사이즈 (철재)', 'type' => 'calculation', 'formula' => 'W0 + 240', 'description' => '철재 케이스 사이즈(S) = 오픈가로(W0) + 240', 'metadata' => ['unit' => 'mm', 'product_type' => 'steel'], 'sort_order' => 2, ], [ 'category_code' => 'CASE', 'variable' => 'CASE_AUTO_SELECT', 'name' => '케이스 자재 자동 선택', 'type' => 'range', 'formula' => null, 'description' => '케이스 자재 길이 및 수량 자동 산출', 'metadata' => ['unit' => 'EA', 'input_variable' => 'S'], 'sort_order' => 3, 'ranges' => [ ['min' => 0, 'max' => 1219, 'result' => '1219 1개', 'item_code' => 'PT-CASE-1219', 'quantity' => 1, 'description' => '0 < S ≤ 1219'], ['min' => 1219, 'max' => 2438, 'result' => '2438 1개', 'item_code' => 'PT-CASE-2438', 'quantity' => 1, 'description' => '1219 < S ≤ 2438'], ['min' => 2438, 'max' => 3000, 'result' => '3000 1개', 'item_code' => 'PT-CASE-3000', 'quantity' => 1, 'description' => '2438 < S ≤ 3000'], ], ], // ============================== // 7. 모터 (MOTOR) - 1개 // ============================== [ 'category_code' => 'MOTOR', 'variable' => 'MOTOR_AUTO_SELECT', 'name' => '모터 자동 선택', 'type' => 'range', 'formula' => null, 'description' => '모터 중량 기반 자동 선택 (전원 유형 반영)', 'metadata' => ['unit' => 'EA', 'input_variable' => 'K'], 'sort_order' => 1, 'ranges' => [ ['min' => 0, 'max' => 150, 'result' => '150k', 'quantity' => 1, 'description' => '0 < K ≤ 150kg'], ['min' => 150, 'max' => 300, 'result' => '300k', 'quantity' => 1, 'description' => '150 < K ≤ 300kg'], ['min' => 300, 'max' => 400, 'result' => '400k', 'quantity' => 1, 'description' => '300 < K ≤ 400kg'], ], ], // ============================== // 8. 제어기 (CONTROLLER) - 1개 // ============================== [ 'category_code' => 'CONTROLLER', 'variable' => 'CTRL_AUTO_SELECT', 'name' => '제어기 자동 선택', 'type' => 'mapping', 'formula' => null, 'description' => '연동제어기 설치 유형(매립/노출)에 따라 자동 선택', 'metadata' => ['unit' => 'EA', 'input_variable' => 'CONTROLLER_TYPE'], 'sort_order' => 1, ], // ============================== // 9. 마구리 (EDGE_WING) - 1개 // ============================== [ 'category_code' => 'EDGE_WING', 'variable' => 'EDGE_QTY', 'name' => '마구리 날개 수량 계산', 'type' => 'calculation', 'formula' => 'EDGE_WING_SIZE / 50', 'description' => '마구리 날개치수 ÷ 50 = 수량', 'metadata' => ['unit' => 'EA'], 'sort_order' => 1, 'is_active' => false, // 미정의 변수(EDGE_WING_SIZE) 사용 ], // ============================== // 10. 검사 (INSPECTION) - 1개 // ============================== [ 'category_code' => 'INSPECTION', 'variable' => 'INSP_FEE', 'name' => '검사비', 'type' => 'calculation', 'formula' => '1', 'description' => '검사비 고정 1EA (단가는 검사비 설정값 적용)', 'metadata' => ['unit' => 'EA'], 'sort_order' => 1, ], // ============================== // 11. 단가수식 (PRICE_FORMULA) - 8개 // ============================== [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_GR', 'name' => '가이드레일 단가 수식', 'type' => 'calculation', 'formula' => 'QTY * UNIT_PRICE', 'description' => '가이드레일 총액 = 수량 × 단가', 'metadata' => ['unit' => '원', 'target' => 'guide_rail'], 'sort_order' => 1, 'is_active' => false, // 미정의 변수(QTY, UNIT_PRICE) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_CASE', 'name' => '케이스 단가 수식', 'type' => 'calculation', 'formula' => 'QTY * UNIT_PRICE', 'description' => '케이스 총액 = 수량 × 단가', 'metadata' => ['unit' => '원', 'target' => 'case'], 'sort_order' => 2, 'is_active' => false, // 미정의 변수(QTY, UNIT_PRICE) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_MOTOR', 'name' => '모터 단가 수식', 'type' => 'calculation', 'formula' => 'QTY * UNIT_PRICE', 'description' => '모터 총액 = 수량 × 단가', 'metadata' => ['unit' => '원', 'target' => 'motor'], 'sort_order' => 3, 'is_active' => false, // 미정의 변수(QTY, UNIT_PRICE) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_CTRL', 'name' => '제어기 단가 수식', 'type' => 'calculation', 'formula' => 'QTY * UNIT_PRICE', 'description' => '제어기 총액 = 수량 × 단가', 'metadata' => ['unit' => '원', 'target' => 'controller'], 'sort_order' => 4, 'is_active' => false, // 미정의 변수(QTY, UNIT_PRICE) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_EDGE', 'name' => '마구리 날개 단가 수식', 'type' => 'calculation', 'formula' => 'QTY * UNIT_PRICE', 'description' => '마구리 날개 총액 = 수량 × 단가', 'metadata' => ['unit' => '원', 'target' => 'edge_wing'], 'sort_order' => 5, 'is_active' => false, // 미정의 변수(QTY, UNIT_PRICE) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_INSP', 'name' => '검사비 단가 수식', 'type' => 'calculation', 'formula' => 'INSPECTION_FEE', 'description' => '검사비 = 입력한 검사비 설정값', 'metadata' => ['unit' => '원', 'target' => 'inspection'], 'sort_order' => 6, 'is_active' => false, // 미정의 변수(INSPECTION_FEE) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_AREA_SCREEN', 'name' => '면적 기반 단가 (스크린)', 'type' => 'calculation', 'formula' => 'AREA * UNIT_PRICE_PER_M2', 'description' => '스크린 면적 기반 총액 = 면적(㎡) × ㎡당 단가', 'metadata' => ['unit' => '원', 'target' => 'area', 'product_type' => 'screen'], 'sort_order' => 7, 'is_active' => false, // 미정의 변수(AREA, UNIT_PRICE_PER_M2) 사용 ], [ 'category_code' => 'PRICE_FORMULA', 'variable' => 'PRICE_AREA_STEEL', 'name' => '면적 기반 단가 (철재)', 'type' => 'calculation', 'formula' => 'AREA * UNIT_PRICE_PER_M2', 'description' => '철재 면적 기반 총액 = 면적(㎡) × ㎡당 단가', 'metadata' => ['unit' => '원', 'target' => 'area', 'product_type' => 'steel'], 'sort_order' => 8, 'is_active' => false, // 미정의 변수(AREA, UNIT_PRICE_PER_M2) 사용 ], ]; $formulaCount = 0; $rangeCount = 0; foreach ($formulas as $formula) { $categoryId = $categoryMap[$formula['category_code']] ?? null; if (! $categoryId) { $this->command->warn("카테고리 '{$formula['category_code']}'를 찾을 수 없습니다: {$formula['variable']}"); continue; } // 수식 생성/업데이트 DB::table('quote_formulas')->updateOrInsert( [ 'tenant_id' => $tenantId, 'variable' => $formula['variable'], ], [ 'tenant_id' => $tenantId, 'category_id' => $categoryId, 'variable' => $formula['variable'], 'name' => $formula['name'], 'type' => $formula['type'], 'formula' => $formula['formula'], 'output_type' => 'variable', 'description' => $formula['description'], 'sort_order' => $formula['sort_order'] ?? 0, 'is_active' => $formula['is_active'] ?? true, 'created_at' => now(), 'updated_at' => now(), ] ); $formulaCount++; // Range 데이터가 있으면 처리 if (! empty($formula['ranges'])) { // 먼저 수식 ID 조회 $formulaRecord = DB::table('quote_formulas') ->where('tenant_id', $tenantId) ->where('variable', $formula['variable']) ->first(); if ($formulaRecord) { // 조건 변수 추출 (metadata에서) $conditionVariable = $formula['metadata']['input_variable'] ?? $formula['variable']; foreach ($formula['ranges'] as $rangeOrder => $range) { // result_value에 추가 정보를 JSON으로 포함 $resultData = [ 'value' => $range['result'], 'item_code' => $range['item_code'] ?? null, 'quantity' => $range['quantity'] ?? 1, 'note' => $range['description'] ?? null, ]; DB::table('quote_formula_ranges')->updateOrInsert( [ 'formula_id' => $formulaRecord->id, 'min_value' => $range['min'], 'max_value' => $range['max'], ], [ 'formula_id' => $formulaRecord->id, 'min_value' => $range['min'], 'max_value' => $range['max'], 'condition_variable' => $conditionVariable, 'result_value' => json_encode($resultData), 'result_type' => 'fixed', 'sort_order' => $rangeOrder + 1, 'created_at' => now(), 'updated_at' => now(), ] ); $rangeCount++; } } } } $this->command->info("QuoteFormulaSeeder: {$formulaCount}개 수식, {$rangeCount}개 범위 데이터 생성 완료"); } }