option('tenant'); $only = $this->option('only'); $fresh = $this->option('fresh'); $this->info("🚀 견적수식 시드 시작 (tenant_id: {$tenantId})"); if ($fresh) { if ($this->confirm('⚠️ 기존 데이터를 모두 삭제하시겠습니까?')) { $this->truncateTables($tenantId); } else { $this->warn('작업이 취소되었습니다.'); return Command::FAILURE; } } $stats = ['categories' => 0, 'formulas' => 0, 'ranges' => 0, 'items' => 0]; // 1. 카테고리 시드 if (! $only || $only === 'categories') { $stats['categories'] = $this->seedCategories($tenantId); } // 2. 수식 시드 if (! $only || $only === 'formulas') { $stats['formulas'] = $this->seedFormulas($tenantId); } // 3. 범위 시드 (수식에 포함된 ranges) if (! $only || $only === 'ranges') { $stats['ranges'] = $this->seedRanges($tenantId); } // 4. 품목 시드 (quote_formula_items) if (! $only || $only === 'items') { $stats['items'] = $this->seedItems($tenantId); } $this->newLine(); $this->info('✅ 견적수식 시드 완료!'); $this->table( ['항목', '개수'], [ ['카테고리', $stats['categories']], ['수식', $stats['formulas']], ['범위', $stats['ranges']], ['품목', $stats['items']], ] ); return Command::SUCCESS; } /** * 카테고리 시드 */ private function seedCategories(int $tenantId): int { $this->info('📁 카테고리 생성 중...'); $categories = [ ['code' => 'INPUT', 'name' => '입력값', 'description' => '견적 산출을 위한 사용자 입력값', 'sort_order' => 0], ['code' => 'OPEN_SIZE', 'name' => '오픈사이즈', 'description' => '자동 견적 산출의 기본 입력값', 'sort_order' => 1], ['code' => 'MAKE_SIZE', 'name' => '제작사이즈', 'description' => '오픈사이즈 기반 제작 치수 계산', 'sort_order' => 2], ['code' => 'AREA', 'name' => '면적', 'description' => '제작사이즈 기반 면적 계산', 'sort_order' => 3], ['code' => 'WEIGHT', 'name' => '중량', 'description' => '면적 기반 중량 계산', 'sort_order' => 4], ['code' => 'GUIDE_RAIL', 'name' => '가이드레일', 'description' => '가이드레일 자재 및 수량 산출', 'sort_order' => 5], ['code' => 'CASE', 'name' => '케이스', 'description' => '케이스 자재 및 수량 산출', 'sort_order' => 6], ['code' => 'MOTOR', 'name' => '모터', 'description' => '모터 규격 자동 선택', 'sort_order' => 7], ['code' => 'CONTROLLER', 'name' => '제어기', 'description' => '제어기 유형 및 자동 선택', 'sort_order' => 8], ['code' => 'EDGE_WING', 'name' => '마구리', 'description' => '마구리 날개 수량 계산', 'sort_order' => 9], ['code' => 'INSPECTION', 'name' => '검사', 'description' => '검사비 계산', 'sort_order' => 10], ['code' => 'PRICE_FORMULA', 'name' => '단가수식', 'description' => '품목별 단가 계산 수식', 'sort_order' => 11], ]; $count = 0; foreach ($categories as $cat) { DB::table('quote_formula_categories')->updateOrInsert( ['tenant_id' => $tenantId, 'code' => $cat['code']], array_merge($cat, [ 'tenant_id' => $tenantId, 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ]) ); $count++; } $this->info(" → {$count}개 카테고리 생성됨"); return $count; } /** * 수식 시드 */ private function seedFormulas(int $tenantId): int { $this->info('📐 수식 생성 중...'); // 카테고리 코드 → ID 매핑 $categoryMap = DB::table('quote_formula_categories') ->where('tenant_id', $tenantId) ->pluck('id', 'code') ->toArray(); if (empty($categoryMap)) { $this->error(' ❌ 카테고리가 없습니다. --only=categories를 먼저 실행하세요.'); return 0; } $formulas = $this->getFormulaData(); $count = 0; foreach ($formulas as $formula) { $categoryId = $categoryMap[$formula['category_code']] ?? null; if (! $categoryId) { $this->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'] ?? null, 'output_type' => 'variable', 'description' => $formula['description'] ?? null, 'sort_order' => $formula['sort_order'] ?? 0, 'is_active' => $formula['is_active'] ?? true, 'created_at' => now(), 'updated_at' => now(), ] ); $count++; } $this->info(" → {$count}개 수식 생성됨"); return $count; } /** * 범위 시드 (수식에 포함된 ranges) */ private function seedRanges(int $tenantId): int { $this->info('📊 범위 데이터 생성 중...'); $formulas = $this->getFormulaData(); $count = 0; foreach ($formulas as $formula) { if (empty($formula['ranges'])) { continue; } // 수식 ID 조회 $formulaRecord = DB::table('quote_formulas') ->where('tenant_id', $tenantId) ->where('variable', $formula['variable']) ->first(); if (! $formulaRecord) { continue; } // 조건 변수 추출 $conditionVariable = $formula['metadata']['input_variable'] ?? $formula['variable']; foreach ($formula['ranges'] as $rangeOrder => $range) { $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(), ] ); $count++; } } $this->info(" → {$count}개 범위 생성됨"); return $count; } /** * 기존 데이터 삭제 */ private function truncateTables(int $tenantId): void { $this->warn('🗑️ 기존 데이터 삭제 중...'); // 순서 중요: FK 제약조건 때문에 역순으로 삭제 $deleted = DB::table('quote_formula_ranges') ->whereIn('formula_id', function ($query) use ($tenantId) { $query->select('id') ->from('quote_formulas') ->where('tenant_id', $tenantId); }) ->delete(); $this->info(" → quote_formula_ranges: {$deleted}개 삭제"); $deleted = DB::table('quote_formula_mappings') ->whereIn('formula_id', function ($query) use ($tenantId) { $query->select('id') ->from('quote_formulas') ->where('tenant_id', $tenantId); }) ->delete(); $this->info(" → quote_formula_mappings: {$deleted}개 삭제"); $deleted = DB::table('quote_formula_items') ->whereIn('formula_id', function ($query) use ($tenantId) { $query->select('id') ->from('quote_formulas') ->where('tenant_id', $tenantId); }) ->delete(); $this->info(" → quote_formula_items: {$deleted}개 삭제"); $deleted = DB::table('quote_formulas') ->where('tenant_id', $tenantId) ->delete(); $this->info(" → quote_formulas: {$deleted}개 삭제"); $deleted = DB::table('quote_formula_categories') ->where('tenant_id', $tenantId) ->delete(); $this->info(" → quote_formula_categories: {$deleted}개 삭제"); } /** * 수식 데이터 정의 (API 시더와 동일) */ private function getFormulaData(): array { return [ // ============================== // 0. 입력값 (INPUT) - Design 사이트 필드와 동일 // ============================== [ 'category_code' => 'INPUT', 'variable' => 'PC', 'name' => '제품카테고리', 'type' => 'input', 'formula' => null, 'description' => '제품 카테고리 선택 (스크린/철재 등)', 'metadata' => [ 'field_type' => 'select', 'options' => [ ['value' => 'screen', 'label' => '스크린'], ['value' => 'steel', 'label' => '철재'], ], ], 'sort_order' => 1, ], [ 'category_code' => 'INPUT', 'variable' => 'PRODUCT_ID', 'name' => '제품명', 'type' => 'input', 'formula' => null, 'description' => '제품 선택 (제품카테고리에 따라 목록 변경)', 'metadata' => ['field_type' => 'select', 'depends_on' => 'PC'], 'sort_order' => 2, ], [ 'category_code' => 'INPUT', 'variable' => 'GT', 'name' => '설치유형', 'type' => 'input', 'formula' => null, 'description' => '설치 유형 선택 (벽면/천장/바닥)', 'metadata' => [ 'field_type' => 'select', 'options' => [ ['value' => 'wall', 'label' => '벽면'], ['value' => 'ceiling', 'label' => '천장'], ['value' => 'floor', 'label' => '바닥'], ], ], 'sort_order' => 3, ], [ 'category_code' => 'INPUT', 'variable' => 'MP', 'name' => '모터전원', 'type' => 'input', 'formula' => null, 'description' => '모터 전원 선택', 'metadata' => [ 'field_type' => 'select', 'options' => [ ['value' => '220V', 'label' => '220V'], ['value' => '380V', 'label' => '380V'], ], ], 'sort_order' => 4, ], [ 'category_code' => 'INPUT', 'variable' => 'CT', 'name' => '제어기', 'type' => 'input', 'formula' => null, 'description' => '제어기 유형 선택 (단독제어/연동제어)', 'metadata' => [ 'field_type' => 'select', 'options' => [ ['value' => 'single', 'label' => '단독제어'], ['value' => 'linked', 'label' => '연동제어'], ], ], 'sort_order' => 5, ], [ 'category_code' => 'INPUT', 'variable' => 'QTY', 'name' => '수량', 'type' => 'input', 'formula' => null, 'description' => '견적 수량', 'metadata' => ['field_type' => 'number', 'min' => 1, 'default' => 1], 'sort_order' => 6, ], // ============================== // 1. 오픈사이즈 (OPEN_SIZE) - 2개 // ============================== [ 'category_code' => 'OPEN_SIZE', 'variable' => 'W0', 'name' => '가로 (W0)', 'type' => 'input', 'formula' => null, 'description' => '오픈사이즈 가로 (mm)', 'metadata' => ['field_type' => 'number', 'unit' => 'mm', 'min' => 100, 'max' => 10000], 'sort_order' => 1, ], [ 'category_code' => 'OPEN_SIZE', 'variable' => 'H0', 'name' => '세로 (H0)', 'type' => 'input', 'formula' => null, 'description' => '오픈사이즈 세로 (mm)', 'metadata' => ['field_type' => 'number', 'unit' => 'mm', 'min' => 100, 'max' => 10000], '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, ], // 제작사이즈 통합 변수 (제품 카테고리 기반) [ 'category_code' => 'MAKE_SIZE', 'variable' => 'W1', 'name' => '제작가로 통합', 'type' => 'calculation', 'formula' => 'W1_SCREEN', // TODO: PC 기반 분기 로직 필요 'description' => '제품 카테고리에 따른 제작 가로 (기본: 스크린)', 'metadata' => ['unit' => 'mm'], 'sort_order' => 5, ], [ 'category_code' => 'MAKE_SIZE', 'variable' => 'H1', 'name' => '제작세로 통합', 'type' => 'calculation', 'formula' => 'H1_SCREEN', // TODO: PC 기반 분기 로직 필요 'description' => '제품 카테고리에 따른 제작 세로 (기본: 스크린)', 'metadata' => ['unit' => 'mm'], 'sort_order' => 6, ], // ============================== // 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) - 3개 // ============================== [ '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, ], // 중량 통합 변수 (모터 선택용) [ 'category_code' => 'WEIGHT', 'variable' => 'K', 'name' => '중량 통합', 'type' => 'calculation', 'formula' => 'K_SCREEN', // TODO: PC 기반 분기 로직 필요 'description' => '제품 카테고리에 따른 중량 (기본: 스크린)', 'metadata' => ['unit' => 'kg'], 'sort_order' => 3, ], // ============================== // 5. 가이드레일 (GUIDE_RAIL) - 2개 (활성) // ============================== [ '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' => 0, 'max' => 1219, 'result' => '1219 2개', 'item_code' => 'PT-GR-1219', 'quantity' => 2, 'description' => '0 < G ≤ 1219'], ['min' => 1219, 'max' => 2438, 'result' => '2438 2개', 'item_code' => 'PT-GR-2438', 'quantity' => 2, 'description' => '1219 < G ≤ 2438'], ['min' => 2438, 'max' => 3000, 'result' => '3000 2개', 'item_code' => 'PT-GR-3000', 'quantity' => 2, 'description' => '2438 < G ≤ 3000'], ['min' => 3000, 'max' => 3600, 'result' => '3600 2개', 'item_code' => 'PT-GR-3600', 'quantity' => 2, 'description' => '3000 < G ≤ 3600'], ], ], // ============================== // 6. 케이스 (CASE) - 4개 // ============================== [ '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' => 'S', 'name' => '케이스 사이즈 통합', 'type' => 'calculation', 'formula' => 'S_SCREEN', // TODO: PC 기반 분기 로직 필요 'description' => '제품 카테고리에 따른 케이스 사이즈 (기본: 스크린)', 'metadata' => ['unit' => 'mm'], 'sort_order' => 3, ], [ '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'], ['min' => 3000, 'max' => 3600, 'result' => '3600 1개', 'item_code' => 'PT-CASE-3600', 'quantity' => 1, 'description' => '3000 < S ≤ 3600'], ['min' => 3600, 'max' => 6000, 'result' => '6000 1개', 'item_code' => 'PT-CASE-6000', 'quantity' => 1, 'description' => '3600 < S ≤ 6000'], ], ], // ============================== // 7. 모터 (MOTOR) - 1개 // ============================== [ 'category_code' => 'MOTOR', 'variable' => 'MOTOR_AUTO_SELECT', 'name' => '모터 자동 선택', 'type' => 'range', 'formula' => null, 'description' => '모터 중량 기반 자동 선택 (5130 실제 규격 기준)', 'metadata' => ['unit' => 'EA', 'input_variable' => 'K'], 'sort_order' => 1, 'ranges' => [ ['min' => 0, 'max' => 150, 'result' => '150K', 'item_code' => 'PT-MOTOR-150', 'quantity' => 1, 'description' => '0 < K ≤ 150kg'], ['min' => 150, 'max' => 300, 'result' => '300K', 'item_code' => 'PT-MOTOR-300', 'quantity' => 1, 'description' => '150 < K ≤ 300kg'], ['min' => 300, 'max' => 400, 'result' => '400K', 'item_code' => 'PT-MOTOR-400', 'quantity' => 1, 'description' => '300 < K ≤ 400kg'], ['min' => 400, 'max' => 500, 'result' => '500K', 'item_code' => 'PT-MOTOR-500', 'quantity' => 1, 'description' => '400 < K ≤ 500kg'], ['min' => 500, 'max' => 600, 'result' => '600K', 'item_code' => 'PT-MOTOR-600', 'quantity' => 1, 'description' => '500 < K ≤ 600kg'], ['min' => 600, 'max' => 800, 'result' => '800K', 'item_code' => 'PT-MOTOR-800', 'quantity' => 1, 'description' => '600 < K ≤ 800kg'], ['min' => 800, 'max' => 1000, 'result' => '1000K', 'item_code' => 'PT-MOTOR-1000', 'quantity' => 1, 'description' => '800 < K ≤ 1000kg'], ['min' => 1000, 'max' => 1500, 'result' => '1500K', 'item_code' => 'PT-MOTOR-1500', 'quantity' => 1, 'description' => '1000 < K ≤ 1500kg'], ['min' => 1500, 'max' => 2000, 'result' => '2000K', 'item_code' => 'PT-MOTOR-2000', 'quantity' => 1, 'description' => '1500 < K ≤ 2000kg'], ], ], // ============================== // 8. 제어기 (CONTROLLER) - 2개 // ============================== [ 'category_code' => 'CONTROLLER', 'variable' => 'CONTROLLER_TYPE', 'name' => '제어기 유형', 'type' => 'input', 'formula' => null, 'description' => '제어기 설치 유형 선택 (매립형/노출형/일체형)', 'sort_order' => 0, ], [ 'category_code' => 'CONTROLLER', 'variable' => 'CTRL_AUTO_SELECT', 'name' => '제어기 자동 선택', 'type' => 'mapping', 'formula' => null, 'description' => '연동제어기 설치 유형(매립/노출)에 따라 자동 선택', 'sort_order' => 1, ], // ============================== // 9. 검사 (INSPECTION) - 1개 // ============================== [ 'category_code' => 'INSPECTION', 'variable' => 'INSP_FEE', 'name' => '검사비', 'type' => 'calculation', 'formula' => '1', 'description' => '검사비 고정 1EA (단가는 검사비 설정값 적용)', 'metadata' => ['unit' => 'EA'], 'sort_order' => 1, ], ]; } /** * 품목 시드 (quote_formula_items) * output_type='item' 수식과 연결된 품목 정의 */ private function seedItems(int $tenantId): int { $this->info('📦 품목 데이터 생성 중...'); // 카테고리 코드 → ID 매핑 $categoryMap = DB::table('quote_formula_categories') ->where('tenant_id', $tenantId) ->pluck('id', 'code') ->toArray(); if (empty($categoryMap)) { $this->error(' ❌ 카테고리가 없습니다.'); return 0; } // 품목 출력용 수식 생성 (output_type = 'item') $itemFormulas = $this->getItemFormulaData(); $formulaMap = []; foreach ($itemFormulas as $formula) { $categoryId = $categoryMap[$formula['category_code']] ?? null; if (! $categoryId) { 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'] ?? null, 'output_type' => 'item', // 품목 출력 'description' => $formula['description'] ?? null, 'sort_order' => $formula['sort_order'] ?? 0, 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ] ); // 수식 ID 조회 $formulaRecord = DB::table('quote_formulas') ->where('tenant_id', $tenantId) ->where('variable', $formula['variable']) ->first(); if ($formulaRecord) { $formulaMap[$formula['variable']] = $formulaRecord->id; } } // 품목 데이터 생성 $items = $this->getItemData(); $count = 0; foreach ($items as $item) { $formulaId = $formulaMap[$item['formula_variable']] ?? null; if (! $formulaId) { $this->warn(" ⚠️ 수식 '{$item['formula_variable']}'를 찾을 수 없음"); continue; } DB::table('quote_formula_items')->updateOrInsert( [ 'formula_id' => $formulaId, 'item_code' => $item['item_code'], ], [ 'formula_id' => $formulaId, 'item_code' => $item['item_code'], 'item_name' => $item['item_name'], 'specification' => $item['specification'] ?? null, 'unit' => $item['unit'] ?? 'EA', 'quantity_formula' => $item['quantity_formula'], 'unit_price_formula' => $item['unit_price_formula'] ?? null, 'sort_order' => $item['sort_order'] ?? 0, 'created_at' => now(), 'updated_at' => now(), ] ); $count++; } $this->info(" → {$count}개 품목 생성됨"); return $count; } /** * 품목 출력용 수식 데이터 * Note: 가이드레일, 케이스, 모터는 range 수식에서 자동 선택되므로 별도 품목 수식 불필요 */ private function getItemFormulaData(): array { return [ // 검사비 품목 출력 (range가 아닌 고정 품목) [ 'category_code' => 'INSPECTION', 'variable' => 'ITEM_INSPECTION', 'name' => '검사비 품목', 'type' => 'calculation', 'formula' => '1', 'description' => '검사비 품목 출력용', 'sort_order' => 10, ], ]; } /** * 품목 데이터 정의 * Note: 가이드레일, 케이스, 모터는 range 수식의 result_value에서 자동 추출됨 */ private function getItemData(): array { return [ // ========== 검사비 품목 ========== [ 'formula_variable' => 'ITEM_INSPECTION', 'item_code' => 'SVC-INSP', 'item_name' => '검사비', 'specification' => null, 'unit' => '식', 'quantity_formula' => 'INSP_FEE', // 검사비 수량 수식 참조 'unit_price_formula' => '50000', // 고정 단가 (테스트용) 'sort_order' => 1, ], ]; } }