diff --git a/database/seeders/QuoteFormulaCategorySeeder.php b/database/seeders/QuoteFormulaCategorySeeder.php new file mode 100644 index 00000000..66133230 --- /dev/null +++ b/database/seeders/QuoteFormulaCategorySeeder.php @@ -0,0 +1,78 @@ + $tenantId, + 'code' => 'input_variables', + 'name' => '입력변수', + 'description' => '사용자가 직접 입력하는 기본 변수 (오픈사이즈, 제품카테고리, 가이드유형, 모터전원, 연동제어, 수량)', + 'sort_order' => 1, + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ], + [ + 'tenant_id' => $tenantId, + 'code' => 'calculation_variables', + 'name' => '계산변수', + 'description' => '입력변수를 기반으로 자동 계산되는 파생 변수 (제작사이즈, 면적, 중량)', + 'sort_order' => 2, + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ], + [ + 'tenant_id' => $tenantId, + 'code' => 'range_selection', + 'name' => '범위선택', + 'description' => '조건(중량, 높이, 폭)에 따라 자동 선택되는 품목 (모터, 가이드레일, 케이스)', + 'sort_order' => 3, + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ], + [ + 'tenant_id' => $tenantId, + 'code' => 'item_mapping', + 'name' => '품목매핑', + 'description' => '견적 계산에 포함되는 품목과 수량/단가 수식 정의', + 'sort_order' => 4, + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ], + ]; + + foreach ($categories as $category) { + DB::table('quote_formula_categories')->updateOrInsert( + ['tenant_id' => $category['tenant_id'], 'code' => $category['code']], + array_merge($category, [ + 'created_at' => now(), + 'updated_at' => now(), + ]) + ); + } + + $this->command->info('QuoteFormulaCategorySeeder: ' . count($categories) . '개 카테고리 생성 완료'); + } +} \ No newline at end of file diff --git a/database/seeders/QuoteFormulaItemSeeder.php b/database/seeders/QuoteFormulaItemSeeder.php new file mode 100644 index 00000000..849e715c --- /dev/null +++ b/database/seeders/QuoteFormulaItemSeeder.php @@ -0,0 +1,175 @@ +where('tenant_id', $tenantId) + ->where('code', 'item_mapping') + ->value('id'); + + if (! $categoryId) { + $this->command->error('QuoteFormulaItemSeeder: 품목매핑 카테고리가 없습니다. QuoteFormulaCategorySeeder를 먼저 실행하세요.'); + + return; + } + + // 완제품 조회 + $products = DB::table('items') + ->where('tenant_id', $tenantId) + ->where('item_type', 'FG') + ->whereIn('code', ['FG-SCR-001', 'FG-STL-001']) + ->pluck('id', 'code') + ->toArray(); + + if (empty($products)) { + $this->command->warn('QuoteFormulaItemSeeder: 완제품이 없습니다. DesignItemSeeder를 먼저 실행하세요.'); + + return; + } + + $formulaItems = []; + + // === FG-SCR-001 (방화스크린 소형) BOM === + if (isset($products['FG-SCR-001'])) { + // 매핑 수식 생성 + $formulaId = DB::table('quote_formulas')->insertGetId([ + 'tenant_id' => $tenantId, + 'category_id' => $categoryId, + 'product_id' => $products['FG-SCR-001'], + 'name' => 'FG-SCR-001 BOM 매핑', + 'variable' => 'BOM_SCR_001', + 'type' => 'mapping', + 'formula' => null, + 'output_type' => 'item', + 'description' => '방화스크린 소형 BOM 품목 매핑', + 'sort_order' => 100, + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => now(), + 'updated_at' => now(), + ]); + + $screenItems = [ + ['code' => 'SF-SCR-F01', 'name' => '스크린 원단', 'spec' => 'A급 방화원단', 'unit' => 'M2', 'qty_formula' => 'W*H/1000000', 'price_formula' => null], + ['code' => 'SF-SCR-F02', 'name' => '가이드레일 (좌)', 'spec' => '알루미늄 프로파일', 'unit' => 'M', 'qty_formula' => 'H/1000', 'price_formula' => null], + ['code' => 'SF-SCR-F03', 'name' => '가이드레일 (우)', 'spec' => '알루미늄 프로파일', 'unit' => 'M', 'qty_formula' => 'H/1000', 'price_formula' => null], + ['code' => 'SF-SCR-F04', 'name' => '케이스', 'spec' => '스틸 도장', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-F05', 'name' => '하부프레임', 'spec' => '스틸 도장', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-M01', 'name' => '모터 (소형)', 'spec' => '350W 220V', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-C01', 'name' => '제어반', 'spec' => '단독형', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-S01', 'name' => '셋팅박스', 'spec' => '표준형', 'unit' => 'SET', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-W01', 'name' => '권선드럼', 'spec' => '표준형', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-B01', 'name' => '브라켓 세트', 'spec' => '벽면형', 'unit' => 'SET', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-SCR-SW01', 'name' => '스위치', 'spec' => '3단 스위치', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SM-B002', 'name' => '볼트 M8x25', 'spec' => 'SUS304', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SM-N002', 'name' => '너트 M8', 'spec' => 'SUS304', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SM-W002', 'name' => '와셔 M8', 'spec' => 'SUS304', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ]; + + foreach ($screenItems as $i => $item) { + $formulaItems[] = [ + 'formula_id' => $formulaId, + 'item_code' => $item['code'], + 'item_name' => $item['name'], + 'specification' => $item['spec'], + 'unit' => $item['unit'], + 'quantity_formula' => $item['qty_formula'], + 'unit_price_formula' => $item['price_formula'], + 'sort_order' => $i + 1, + ]; + } + } + + // === FG-STL-001 (철재도어 소형) BOM === + if (isset($products['FG-STL-001'])) { + // 매핑 수식 생성 + $formulaId = DB::table('quote_formulas')->insertGetId([ + 'tenant_id' => $tenantId, + 'category_id' => $categoryId, + 'product_id' => $products['FG-STL-001'], + 'name' => 'FG-STL-001 BOM 매핑', + 'variable' => 'BOM_STL_001', + 'type' => 'mapping', + 'formula' => null, + 'output_type' => 'item', + 'description' => '철재도어 소형 BOM 품목 매핑', + 'sort_order' => 200, + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => now(), + 'updated_at' => now(), + ]); + + $steelItems = [ + ['code' => 'SF-STL-P01', 'name' => '도어 패널', 'spec' => '1.2T 강판', 'unit' => 'M2', 'qty_formula' => 'W*H/1000000', 'price_formula' => null], + ['code' => 'SF-STL-F01', 'name' => '문틀 프레임', 'spec' => '1.6T 강판', 'unit' => 'M', 'qty_formula' => '(W+H)*2/1000', 'price_formula' => null], + ['code' => 'SF-STL-G01', 'name' => '유리창', 'spec' => '방화유리', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-STL-H01', 'name' => '힌지', 'spec' => '스틸 힌지', 'unit' => 'SET', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-STL-L01', 'name' => '잠금장치', 'spec' => '레버형', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-STL-C01', 'name' => '도어클로저', 'spec' => '유압식', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SF-STL-S01', 'name' => '실링재', 'spec' => '방화 패킹', 'unit' => 'M', 'qty_formula' => '(W+H)*2/1000', 'price_formula' => null], + ['code' => 'SF-STL-PT01', 'name' => '파우더 도장', 'spec' => '방청도장', 'unit' => 'M2', 'qty_formula' => 'W*H/1000000*2', 'price_formula' => null], + ['code' => 'SM-B002', 'name' => '볼트 M8x25', 'spec' => 'SUS304', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ['code' => 'SM-N002', 'name' => '너트 M8', 'spec' => 'SUS304', 'unit' => 'EA', 'qty_formula' => '1', 'price_formula' => null], + ]; + + foreach ($steelItems as $i => $item) { + $formulaItems[] = [ + 'formula_id' => $formulaId, + 'item_code' => $item['code'], + 'item_name' => $item['name'], + 'specification' => $item['spec'], + 'unit' => $item['unit'], + 'quantity_formula' => $item['qty_formula'], + 'unit_price_formula' => $item['price_formula'], + 'sort_order' => $i + 1, + ]; + } + } + + // 데이터 삽입 + foreach ($formulaItems as $item) { + DB::table('quote_formula_items')->insert(array_merge($item, [ + 'created_at' => now(), + 'updated_at' => now(), + ])); + } + + $this->command->info('QuoteFormulaItemSeeder: ' . count($formulaItems) . '개 품목 매핑 생성 완료'); + $this->command->info(' - FG-SCR-001: ' . (isset($products['FG-SCR-001']) ? '14개 품목' : '미생성')); + $this->command->info(' - FG-STL-001: ' . (isset($products['FG-STL-001']) ? '10개 품목' : '미생성')); + } +} \ No newline at end of file diff --git a/database/seeders/QuoteFormulaRangeSeeder.php b/database/seeders/QuoteFormulaRangeSeeder.php new file mode 100644 index 00000000..23d32e8c --- /dev/null +++ b/database/seeders/QuoteFormulaRangeSeeder.php @@ -0,0 +1,136 @@ +where('tenant_id', $tenantId) + ->where('type', 'range') + ->whereIn('variable', ['MOTOR', 'GUIDE', 'CASE']) + ->pluck('id', 'variable') + ->toArray(); + + if (empty($rangeFormulas)) { + $this->command->error('QuoteFormulaRangeSeeder: 범위선택 수식이 없습니다. QuoteFormulaSeeder를 먼저 실행하세요.'); + + return; + } + + $ranges = []; + + // === 모터 선택 (중량 K 기준) === + if (isset($rangeFormulas['MOTOR'])) { + $motorRanges = [ + ['min' => 0, 'max' => 30, 'result' => 'SF-SCR-M01', 'note' => '소형모터 350W'], + ['min' => 30, 'max' => 50, 'result' => 'SF-SCR-M02', 'note' => '중형모터 500W'], + ['min' => 50, 'max' => 80, 'result' => 'SF-SCR-M03', 'note' => '대형모터 750W'], + ['min' => 80, 'max' => 9999, 'result' => 'SF-SCR-M04', 'note' => '특대모터 1000W'], + ]; + + foreach ($motorRanges as $i => $range) { + $ranges[] = [ + 'formula_id' => $rangeFormulas['MOTOR'], + 'min_value' => $range['min'], + 'max_value' => $range['max'], + 'condition_variable' => 'K', + 'result_value' => $range['result'], + 'result_type' => 'fixed', + 'sort_order' => $i + 1, + ]; + } + } + + // === 가이드레일 선택 (높이 H 기준) === + if (isset($rangeFormulas['GUIDE'])) { + $guideRanges = [ + ['min' => 0, 'max' => 2500, 'result' => 'SF-SCR-F02', 'note' => '가이드레일 소형'], + ['min' => 2500, 'max' => 3500, 'result' => 'SF-SCR-F02', 'note' => '가이드레일 중형'], + ['min' => 3500, 'max' => 4500, 'result' => 'SF-SCR-F02', 'note' => '가이드레일 대형'], + ['min' => 4500, 'max' => 9999, 'result' => 'SF-SCR-F02', 'note' => '가이드레일 특대'], + ]; + + foreach ($guideRanges as $i => $range) { + $ranges[] = [ + 'formula_id' => $rangeFormulas['GUIDE'], + 'min_value' => $range['min'], + 'max_value' => $range['max'], + 'condition_variable' => 'H', + 'result_value' => $range['result'], + 'result_type' => 'fixed', + 'sort_order' => $i + 1, + ]; + } + } + + // === 케이스 선택 (폭 W 기준) === + if (isset($rangeFormulas['CASE'])) { + $caseRanges = [ + ['min' => 0, 'max' => 2000, 'result' => 'SF-SCR-F04', 'note' => '케이스 소형'], + ['min' => 2000, 'max' => 3000, 'result' => 'SF-SCR-F04', 'note' => '케이스 중형'], + ['min' => 3000, 'max' => 4000, 'result' => 'SF-SCR-F04', 'note' => '케이스 대형'], + ['min' => 4000, 'max' => 9999, 'result' => 'SF-SCR-F04', 'note' => '케이스 특대'], + ]; + + foreach ($caseRanges as $i => $range) { + $ranges[] = [ + 'formula_id' => $rangeFormulas['CASE'], + 'min_value' => $range['min'], + 'max_value' => $range['max'], + 'condition_variable' => 'W', + 'result_value' => $range['result'], + 'result_type' => 'fixed', + 'sort_order' => $i + 1, + ]; + } + } + + // 기존 데이터 삭제 후 삽입 + DB::table('quote_formula_ranges') + ->whereIn('formula_id', array_values($rangeFormulas)) + ->delete(); + + foreach ($ranges as $range) { + DB::table('quote_formula_ranges')->insert(array_merge($range, [ + 'created_at' => now(), + 'updated_at' => now(), + ])); + } + + $this->command->info('QuoteFormulaRangeSeeder: ' . count($ranges) . '개 범위 조건 생성 완료'); + $this->command->info(' - 모터 선택: ' . (isset($rangeFormulas['MOTOR']) ? '4개' : '0개')); + $this->command->info(' - 가이드레일 선택: ' . (isset($rangeFormulas['GUIDE']) ? '4개' : '0개')); + $this->command->info(' - 케이스 선택: ' . (isset($rangeFormulas['CASE']) ? '4개' : '0개')); + } +} \ No newline at end of file diff --git a/database/seeders/QuoteFormulaSeeder.php b/database/seeders/QuoteFormulaSeeder.php new file mode 100644 index 00000000..dacbf577 --- /dev/null +++ b/database/seeders/QuoteFormulaSeeder.php @@ -0,0 +1,282 @@ +where('tenant_id', $tenantId) + ->whereIn('code', ['input_variables', 'calculation_variables', 'range_selection']) + ->pluck('id', 'code') + ->toArray(); + + if (empty($categories)) { + $this->command->error('QuoteFormulaSeeder: 카테고리가 없습니다. QuoteFormulaCategorySeeder를 먼저 실행하세요.'); + + return; + } + + $formulas = []; + + // === 1. 입력변수 (input) === + $inputVars = [ + [ + 'variable' => 'PC', + 'name' => '제품 카테고리', + 'description' => '스크린 또는 철재 선택', + 'formula' => null, + 'sort_order' => 1, + ], + [ + 'variable' => 'W0', + 'name' => '오픈사이즈 폭 (W0)', + 'description' => '개구부 실측 폭 (mm)', + 'formula' => null, + 'sort_order' => 2, + ], + [ + 'variable' => 'H0', + 'name' => '오픈사이즈 높이 (H0)', + 'description' => '개구부 실측 높이 (mm)', + 'formula' => null, + 'sort_order' => 3, + ], + [ + 'variable' => 'GT', + 'name' => '가이드레일 설치유형', + 'description' => '벽면형 또는 측면형', + 'formula' => null, + 'sort_order' => 4, + ], + [ + 'variable' => 'MP', + 'name' => '모터 전원', + 'description' => '220V 또는 380V', + 'formula' => null, + 'sort_order' => 5, + ], + [ + 'variable' => 'CT', + 'name' => '연동제어기', + 'description' => '단독 또는 연동', + 'formula' => null, + 'sort_order' => 6, + ], + [ + 'variable' => 'QTY', + 'name' => '수량', + 'description' => '견적 수량', + 'formula' => null, + 'sort_order' => 7, + ], + ]; + + foreach ($inputVars as $var) { + $formulas[] = [ + 'tenant_id' => $tenantId, + 'category_id' => $categories['input_variables'], + 'product_id' => null, + 'name' => $var['name'], + 'variable' => $var['variable'], + 'type' => 'input', + 'formula' => $var['formula'], + 'output_type' => 'variable', + 'description' => $var['description'], + 'sort_order' => $var['sort_order'], + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ]; + } + + // === 2. 계산변수 (calculation) === + $calcVars = [ + [ + 'variable' => 'W1_SCREEN', + 'name' => '제작폭 W1 (스크린)', + 'description' => '스크린 제작 폭: W0 + 140mm', + 'formula' => 'W0 + 140', + 'sort_order' => 10, + ], + [ + 'variable' => 'W1_STEEL', + 'name' => '제작폭 W1 (철재)', + 'description' => '철재 제작 폭: W0 + 110mm', + 'formula' => 'W0 + 110', + 'sort_order' => 11, + ], + [ + 'variable' => 'H1', + 'name' => '제작높이 H1', + 'description' => '제작 높이: H0 + 350mm', + 'formula' => 'H0 + 350', + 'sort_order' => 12, + ], + [ + 'variable' => 'W', + 'name' => '제작폭 (W)', + 'description' => 'W1의 별칭 (수식에서 사용)', + 'formula' => 'IF(PC=="스크린", W0+140, W0+110)', + 'sort_order' => 13, + ], + [ + 'variable' => 'H', + 'name' => '제작높이 (H)', + 'description' => 'H1의 별칭 (수식에서 사용)', + 'formula' => 'H0 + 350', + 'sort_order' => 14, + ], + [ + 'variable' => 'M', + 'name' => '면적 (M)', + 'description' => '제작 면적 (㎡): W × H / 1,000,000', + 'formula' => 'W * H / 1000000', + 'sort_order' => 15, + ], + [ + 'variable' => 'K_SCREEN', + 'name' => '중량 K (스크린)', + 'description' => '스크린 중량 (kg): M×2 + W0/1000×14.17', + 'formula' => 'M * 2 + W0 / 1000 * 14.17', + 'sort_order' => 16, + ], + [ + 'variable' => 'K_STEEL', + 'name' => '중량 K (철재)', + 'description' => '철재 중량 (kg): M × 25', + 'formula' => 'M * 25', + 'sort_order' => 17, + ], + [ + 'variable' => 'K', + 'name' => '중량 (K)', + 'description' => '제품별 중량 선택', + 'formula' => 'IF(PC=="스크린", M*2+W0/1000*14.17, M*25)', + 'sort_order' => 18, + ], + ]; + + foreach ($calcVars as $var) { + $formulas[] = [ + 'tenant_id' => $tenantId, + 'category_id' => $categories['calculation_variables'], + 'product_id' => null, + 'name' => $var['name'], + 'variable' => $var['variable'], + 'type' => 'calculation', + 'formula' => $var['formula'], + 'output_type' => 'variable', + 'description' => $var['description'], + 'sort_order' => $var['sort_order'], + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ]; + } + + // === 3. 범위선택 (range) === + $rangeVars = [ + [ + 'variable' => 'MOTOR', + 'name' => '모터 자동선택', + 'description' => '중량(K) 기준 모터 자동 선택', + 'formula' => 'K', // 조건 변수 + 'sort_order' => 20, + ], + [ + 'variable' => 'GUIDE', + 'name' => '가이드레일 자동선택', + 'description' => '높이(H) 기준 가이드레일 규격 선택', + 'formula' => 'H', + 'sort_order' => 21, + ], + [ + 'variable' => 'CASE', + 'name' => '케이스 자동선택', + 'description' => '폭(W) 기준 케이스 규격 선택', + 'formula' => 'W', + 'sort_order' => 22, + ], + ]; + + foreach ($rangeVars as $var) { + $formulas[] = [ + 'tenant_id' => $tenantId, + 'category_id' => $categories['range_selection'], + 'product_id' => null, + 'name' => $var['name'], + 'variable' => $var['variable'], + 'type' => 'range', + 'formula' => $var['formula'], + 'output_type' => 'item', + 'description' => $var['description'], + 'sort_order' => $var['sort_order'], + 'is_active' => true, + 'created_by' => 1, + 'updated_by' => 1, + ]; + } + + // 기존 데이터 삭제 후 삽입 + $existingIds = DB::table('quote_formulas') + ->where('tenant_id', $tenantId) + ->whereIn('category_id', array_values($categories)) + ->pluck('id') + ->toArray(); + + if (! empty($existingIds)) { + // 연결된 범위/아이템 데이터도 삭제 + DB::table('quote_formula_ranges')->whereIn('formula_id', $existingIds)->delete(); + DB::table('quote_formula_items')->whereIn('formula_id', $existingIds)->delete(); + DB::table('quote_formulas')->whereIn('id', $existingIds)->delete(); + } + + // 새 데이터 삽입 + foreach ($formulas as $formula) { + DB::table('quote_formulas')->insert(array_merge($formula, [ + 'created_at' => now(), + 'updated_at' => now(), + ])); + } + + $this->command->info('QuoteFormulaSeeder: ' . count($formulas) . '개 수식 생성 완료'); + $this->command->info(' - 입력변수: ' . count($inputVars) . '개'); + $this->command->info(' - 계산변수: ' . count($calcVars) . '개'); + $this->command->info(' - 범위선택: ' . count($rangeVars) . '개'); + } +} \ No newline at end of file