- DB 연결: 로컬/Docker 환경 오버라이딩 설정 (.env) - 테넌트 위젯: redirect 버그 수정 (TenantSelectorWidget) - 통계 위젯: 사용자/제품/자재/주문 카드 추가 (StatsOverviewWidget) - 리소스 한국어화: Product, Material 모델 레이블 추가 - 대시보드: 위젯 등록 및 캐시 최적화 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
574 lines
20 KiB
PHP
574 lines
20 KiB
PHP
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use Illuminate\Database\Seeder;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Models\Category;
|
|
use App\Models\Product;
|
|
use App\Models\Material;
|
|
use App\Models\Design\DesignModel;
|
|
use App\Models\Design\ModelVersion;
|
|
use App\Models\Design\ModelParameter;
|
|
use App\Models\Design\ModelFormula;
|
|
use App\Models\Design\BomConditionRule;
|
|
use App\Models\Design\BomTemplate;
|
|
use App\Models\Design\BomTemplateItem;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
class KSS01ModelSeeder extends Seeder
|
|
{
|
|
/**
|
|
* Run the database seeds for KSS01 specific model.
|
|
* This creates the exact KSS01 screen door model as referenced in the codebase.
|
|
*/
|
|
public function run(): void
|
|
{
|
|
// Get or create test tenant
|
|
$tenant = Tenant::firstOrCreate(
|
|
['code' => 'KSS_DEMO'],
|
|
[
|
|
'name' => 'KSS Demo Tenant',
|
|
'description' => 'Demonstration tenant for KSS01 model',
|
|
'is_active' => true
|
|
]
|
|
);
|
|
|
|
// Get or create test user
|
|
$user = User::firstOrCreate(
|
|
['email' => 'demo@kss01.com'],
|
|
[
|
|
'name' => 'KSS01 Demo User',
|
|
'password' => Hash::make('kss01demo'),
|
|
'email_verified_at' => now()
|
|
]
|
|
);
|
|
|
|
// Associate user with tenant
|
|
if (!$user->tenants()->where('tenant_id', $tenant->id)->exists()) {
|
|
$user->tenants()->attach($tenant->id, [
|
|
'is_active' => true,
|
|
'is_default' => true
|
|
]);
|
|
}
|
|
|
|
// Create screen door category
|
|
$category = Category::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'code' => 'SCREEN_DOORS'
|
|
],
|
|
[
|
|
'name' => 'Screen Doors',
|
|
'description' => 'Automatic screen door systems',
|
|
'is_active' => true
|
|
]
|
|
);
|
|
|
|
// Create KSS01 specific materials and products
|
|
$this->createKSS01Materials($tenant);
|
|
$this->createKSS01Products($tenant);
|
|
|
|
// Create the KSS01 model
|
|
$kss01Model = DesignModel::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'code' => 'KSS01'
|
|
],
|
|
[
|
|
'name' => 'KSS01 Screen Door System',
|
|
'category_id' => $category->id,
|
|
'lifecycle' => 'ACTIVE',
|
|
'description' => 'Production KSS01 automatic screen door system with parametric BOM',
|
|
'is_active' => true
|
|
]
|
|
);
|
|
|
|
// Create KSS01 parameters (matching the codebase expectations)
|
|
$this->createKSS01Parameters($tenant, $kss01Model);
|
|
|
|
// Create KSS01 formulas (matching the service expectations)
|
|
$this->createKSS01Formulas($tenant, $kss01Model);
|
|
|
|
// Create KSS01 condition rules
|
|
$this->createKSS01ConditionRules($tenant, $kss01Model);
|
|
|
|
// Create BOM template
|
|
$this->createKSS01BomTemplate($tenant, $kss01Model);
|
|
|
|
$this->command->info('KSS01 model seeded successfully!');
|
|
$this->command->info('Demo tenant: ' . $tenant->code);
|
|
$this->command->info('Demo user: ' . $user->email . ' / password: kss01demo');
|
|
}
|
|
|
|
/**
|
|
* Create KSS01 specific materials
|
|
*/
|
|
private function createKSS01Materials(Tenant $tenant): void
|
|
{
|
|
$materials = [
|
|
[
|
|
'code' => 'FABRIC_KSS01',
|
|
'name' => 'KSS01 Fabric Screen',
|
|
'description' => 'Specialized fabric for KSS01 screen door',
|
|
'unit' => 'M2',
|
|
'density' => 0.35,
|
|
'color' => 'CHARCOAL'
|
|
],
|
|
[
|
|
'code' => 'STEEL_KSS01',
|
|
'name' => 'KSS01 Steel Mesh',
|
|
'description' => 'Security steel mesh for KSS01',
|
|
'unit' => 'M2',
|
|
'density' => 2.8,
|
|
'color' => 'GRAPHITE'
|
|
],
|
|
[
|
|
'code' => 'RAIL_KSS01_GUIDE',
|
|
'name' => 'KSS01 Guide Rail',
|
|
'description' => 'Precision guide rail for KSS01 system',
|
|
'unit' => 'M',
|
|
'density' => 1.2,
|
|
'color' => 'ANODIZED'
|
|
],
|
|
[
|
|
'code' => 'CABLE_KSS01_LIFT',
|
|
'name' => 'KSS01 Lift Cable',
|
|
'description' => 'High-strength lift cable for KSS01',
|
|
'unit' => 'M',
|
|
'density' => 0.15,
|
|
'color' => 'STAINLESS'
|
|
],
|
|
[
|
|
'code' => 'SEAL_KSS01_WEATHER',
|
|
'name' => 'KSS01 Weather Seal',
|
|
'description' => 'Weather sealing strip for KSS01',
|
|
'unit' => 'M',
|
|
'density' => 0.08,
|
|
'color' => 'BLACK'
|
|
]
|
|
];
|
|
|
|
foreach ($materials as $materialData) {
|
|
Material::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'code' => $materialData['code']
|
|
],
|
|
array_merge($materialData, [
|
|
'is_active' => true,
|
|
'created_by' => 1
|
|
])
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create KSS01 specific products
|
|
*/
|
|
private function createKSS01Products(Tenant $tenant): void
|
|
{
|
|
$products = [
|
|
[
|
|
'code' => 'BRACKET_KSS01_WALL',
|
|
'name' => 'KSS01 Wall Bracket',
|
|
'description' => 'Heavy-duty wall mounting bracket for KSS01',
|
|
'unit' => 'EA',
|
|
'weight' => 0.8,
|
|
'color' => 'POWDER_COATED'
|
|
],
|
|
[
|
|
'code' => 'BRACKET_KSS01_CEILING',
|
|
'name' => 'KSS01 Ceiling Bracket',
|
|
'description' => 'Ceiling mounting bracket for KSS01',
|
|
'unit' => 'EA',
|
|
'weight' => 1.0,
|
|
'color' => 'POWDER_COATED'
|
|
],
|
|
[
|
|
'code' => 'MOTOR_KSS01_STD',
|
|
'name' => 'KSS01 Standard Motor',
|
|
'description' => 'Standard 12V motor for KSS01 (up to 4m²)',
|
|
'unit' => 'EA',
|
|
'weight' => 2.8,
|
|
'color' => 'BLACK'
|
|
],
|
|
[
|
|
'code' => 'MOTOR_KSS01_HEAVY',
|
|
'name' => 'KSS01 Heavy Duty Motor',
|
|
'description' => 'Heavy duty 24V motor for large KSS01 systems',
|
|
'unit' => 'EA',
|
|
'weight' => 4.2,
|
|
'color' => 'BLACK'
|
|
],
|
|
[
|
|
'code' => 'CONTROLLER_KSS01',
|
|
'name' => 'KSS01 Smart Controller',
|
|
'description' => 'Smart controller with app connectivity for KSS01',
|
|
'unit' => 'EA',
|
|
'weight' => 0.25,
|
|
'color' => 'WHITE'
|
|
],
|
|
[
|
|
'code' => 'CASE_KSS01_HEAD',
|
|
'name' => 'KSS01 Head Case',
|
|
'description' => 'Aluminum head case housing for KSS01',
|
|
'unit' => 'EA',
|
|
'weight' => 2.5,
|
|
'color' => 'ANODIZED'
|
|
],
|
|
[
|
|
'code' => 'BOTTOM_BAR_KSS01',
|
|
'name' => 'KSS01 Bottom Bar',
|
|
'description' => 'Weighted bottom bar for KSS01 screen',
|
|
'unit' => 'EA',
|
|
'weight' => 1.5,
|
|
'color' => 'ANODIZED'
|
|
],
|
|
[
|
|
'code' => 'PIPE_KSS01_ROLLER',
|
|
'name' => 'KSS01 Roller Pipe',
|
|
'description' => 'Precision roller pipe for KSS01 screen',
|
|
'unit' => 'EA',
|
|
'weight' => 1.0,
|
|
'color' => 'ANODIZED'
|
|
]
|
|
];
|
|
|
|
foreach ($products as $productData) {
|
|
Product::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'code' => $productData['code']
|
|
],
|
|
array_merge($productData, [
|
|
'is_active' => true,
|
|
'created_by' => 1
|
|
])
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create KSS01 parameters (matching BomResolverService expectations)
|
|
*/
|
|
private function createKSS01Parameters(Tenant $tenant, DesignModel $model): void
|
|
{
|
|
$parameters = [
|
|
[
|
|
'parameter_name' => 'W0',
|
|
'parameter_type' => 'NUMBER',
|
|
'is_required' => true,
|
|
'default_value' => '800',
|
|
'min_value' => 600,
|
|
'max_value' => 3000,
|
|
'unit' => 'mm',
|
|
'description' => 'Opening width (clear opening)',
|
|
'sort_order' => 1
|
|
],
|
|
[
|
|
'parameter_name' => 'H0',
|
|
'parameter_type' => 'NUMBER',
|
|
'is_required' => true,
|
|
'default_value' => '600',
|
|
'min_value' => 400,
|
|
'max_value' => 2500,
|
|
'unit' => 'mm',
|
|
'description' => 'Opening height (clear opening)',
|
|
'sort_order' => 2
|
|
],
|
|
[
|
|
'parameter_name' => 'screen_type',
|
|
'parameter_type' => 'SELECT',
|
|
'is_required' => true,
|
|
'default_value' => 'FABRIC',
|
|
'options' => ['FABRIC', 'STEEL'],
|
|
'description' => 'Screen material type',
|
|
'sort_order' => 3
|
|
],
|
|
[
|
|
'parameter_name' => 'install_type',
|
|
'parameter_type' => 'SELECT',
|
|
'is_required' => true,
|
|
'default_value' => 'WALL',
|
|
'options' => ['WALL', 'CEILING', 'RECESSED'],
|
|
'description' => 'Installation method',
|
|
'sort_order' => 4
|
|
],
|
|
[
|
|
'parameter_name' => 'power_source',
|
|
'parameter_type' => 'SELECT',
|
|
'is_required' => false,
|
|
'default_value' => 'AC',
|
|
'options' => ['AC', 'DC', 'BATTERY'],
|
|
'description' => 'Power source type',
|
|
'sort_order' => 5
|
|
]
|
|
];
|
|
|
|
foreach ($parameters as $paramData) {
|
|
ModelParameter::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'model_id' => $model->id,
|
|
'parameter_name' => $paramData['parameter_name']
|
|
],
|
|
$paramData
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create KSS01 formulas (matching BomResolverService::resolveKSS01)
|
|
*/
|
|
private function createKSS01Formulas(Tenant $tenant, DesignModel $model): void
|
|
{
|
|
$formulas = [
|
|
[
|
|
'formula_name' => 'W1',
|
|
'expression' => 'W0 + 100',
|
|
'description' => 'Overall width (opening + frame)',
|
|
'sort_order' => 1
|
|
],
|
|
[
|
|
'formula_name' => 'H1',
|
|
'expression' => 'H0 + 100',
|
|
'description' => 'Overall height (opening + frame)',
|
|
'sort_order' => 2
|
|
],
|
|
[
|
|
'formula_name' => 'area',
|
|
'expression' => '(W1 * H1) / 1000000',
|
|
'description' => 'Total area in square meters',
|
|
'sort_order' => 3
|
|
],
|
|
[
|
|
'formula_name' => 'weight',
|
|
'expression' => 'area * 8 + W1 / 1000 * 2.5',
|
|
'description' => 'Estimated total weight in kg',
|
|
'sort_order' => 4
|
|
],
|
|
[
|
|
'formula_name' => 'motor_load',
|
|
'expression' => 'weight * 1.5 + area * 3',
|
|
'description' => 'Motor load calculation',
|
|
'sort_order' => 5
|
|
],
|
|
[
|
|
'formula_name' => 'bracket_count',
|
|
'expression' => 'if(W1 > 1000, 3, 2)',
|
|
'description' => 'Number of brackets required',
|
|
'sort_order' => 6
|
|
],
|
|
[
|
|
'formula_name' => 'rail_length',
|
|
'expression' => '(W1 + H1) * 2 / 1000',
|
|
'description' => 'Guide rail length in meters',
|
|
'sort_order' => 7
|
|
]
|
|
];
|
|
|
|
foreach ($formulas as $formulaData) {
|
|
ModelFormula::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'model_id' => $model->id,
|
|
'formula_name' => $formulaData['formula_name']
|
|
],
|
|
$formulaData
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create KSS01 condition rules
|
|
*/
|
|
private function createKSS01ConditionRules(Tenant $tenant, DesignModel $model): void
|
|
{
|
|
$rules = [
|
|
// Screen material selection
|
|
[
|
|
'rule_name' => 'Fabric Screen Material',
|
|
'condition_expression' => 'screen_type == "FABRIC"',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'MATERIAL',
|
|
'target_id' => Material::where('tenant_id', $tenant->id)->where('code', 'FABRIC_KSS01')->first()->id,
|
|
'quantity_multiplier' => 'area',
|
|
'description' => 'Include fabric screen material',
|
|
'sort_order' => 1
|
|
],
|
|
[
|
|
'rule_name' => 'Steel Screen Material',
|
|
'condition_expression' => 'screen_type == "STEEL"',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'MATERIAL',
|
|
'target_id' => Material::where('tenant_id', $tenant->id)->where('code', 'STEEL_KSS01')->first()->id,
|
|
'quantity_multiplier' => 'area',
|
|
'description' => 'Include steel mesh material',
|
|
'sort_order' => 2
|
|
],
|
|
// Motor selection based on load
|
|
[
|
|
'rule_name' => 'Standard Motor',
|
|
'condition_expression' => 'motor_load <= 20',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'PRODUCT',
|
|
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'MOTOR_KSS01_STD')->first()->id,
|
|
'quantity_multiplier' => 1,
|
|
'description' => 'Standard motor for normal loads',
|
|
'sort_order' => 3
|
|
],
|
|
[
|
|
'rule_name' => 'Heavy Duty Motor',
|
|
'condition_expression' => 'motor_load > 20',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'PRODUCT',
|
|
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'MOTOR_KSS01_HEAVY')->first()->id,
|
|
'quantity_multiplier' => 1,
|
|
'description' => 'Heavy duty motor for high loads',
|
|
'sort_order' => 4
|
|
],
|
|
// Bracket selection based on installation
|
|
[
|
|
'rule_name' => 'Wall Brackets',
|
|
'condition_expression' => 'install_type == "WALL"',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'PRODUCT',
|
|
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'BRACKET_KSS01_WALL')->first()->id,
|
|
'quantity_multiplier' => 'bracket_count',
|
|
'description' => 'Wall mounting brackets',
|
|
'sort_order' => 5
|
|
],
|
|
[
|
|
'rule_name' => 'Ceiling Brackets',
|
|
'condition_expression' => 'install_type == "CEILING"',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'PRODUCT',
|
|
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'BRACKET_KSS01_CEILING')->first()->id,
|
|
'quantity_multiplier' => 'bracket_count',
|
|
'description' => 'Ceiling mounting brackets',
|
|
'sort_order' => 6
|
|
],
|
|
// Guide rail
|
|
[
|
|
'rule_name' => 'Guide Rail',
|
|
'condition_expression' => 'true', // Always include
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'MATERIAL',
|
|
'target_id' => Material::where('tenant_id', $tenant->id)->where('code', 'RAIL_KSS01_GUIDE')->first()->id,
|
|
'quantity_multiplier' => 'rail_length',
|
|
'description' => 'Guide rail for screen movement',
|
|
'sort_order' => 7
|
|
],
|
|
// Weather sealing for large openings
|
|
[
|
|
'rule_name' => 'Weather Seal for Large Openings',
|
|
'condition_expression' => 'area > 3.0',
|
|
'action_type' => 'INCLUDE',
|
|
'target_type' => 'MATERIAL',
|
|
'target_id' => Material::where('tenant_id', $tenant->id)->where('code', 'SEAL_KSS01_WEATHER')->first()->id,
|
|
'quantity_multiplier' => 'rail_length',
|
|
'description' => 'Weather sealing for large openings',
|
|
'sort_order' => 8
|
|
]
|
|
];
|
|
|
|
foreach ($rules as $ruleData) {
|
|
BomConditionRule::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'model_id' => $model->id,
|
|
'rule_name' => $ruleData['rule_name']
|
|
],
|
|
$ruleData
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create KSS01 BOM template
|
|
*/
|
|
private function createKSS01BomTemplate(Tenant $tenant, DesignModel $model): void
|
|
{
|
|
// Create model version
|
|
$modelVersion = ModelVersion::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'model_id' => $model->id,
|
|
'version_no' => '1.0'
|
|
],
|
|
[
|
|
'status' => 'RELEASED',
|
|
'description' => 'Initial release of KSS01',
|
|
'created_by' => 1
|
|
]
|
|
);
|
|
|
|
// Create BOM template
|
|
$bomTemplate = BomTemplate::firstOrCreate(
|
|
[
|
|
'tenant_id' => $tenant->id,
|
|
'model_version_id' => $modelVersion->id,
|
|
'name' => 'KSS01 Base BOM'
|
|
],
|
|
[
|
|
'description' => 'Base BOM template for KSS01 screen door system',
|
|
'is_active' => true,
|
|
'created_by' => 1
|
|
]
|
|
);
|
|
|
|
// Create base BOM items (always included components)
|
|
$baseItems = [
|
|
[
|
|
'ref_type' => 'PRODUCT',
|
|
'ref_id' => Product::where('tenant_id', $tenant->id)->where('code', 'CONTROLLER_KSS01')->first()->id,
|
|
'quantity' => 1,
|
|
'waste_rate' => 0,
|
|
'order' => 1
|
|
],
|
|
[
|
|
'ref_type' => 'PRODUCT',
|
|
'ref_id' => Product::where('tenant_id', $tenant->id)->where('code', 'CASE_KSS01_HEAD')->first()->id,
|
|
'quantity' => 1,
|
|
'waste_rate' => 0,
|
|
'order' => 2
|
|
],
|
|
[
|
|
'ref_type' => 'PRODUCT',
|
|
'ref_id' => Product::where('tenant_id', $tenant->id)->where('code', 'BOTTOM_BAR_KSS01')->first()->id,
|
|
'quantity' => 1,
|
|
'waste_rate' => 0,
|
|
'order' => 3
|
|
],
|
|
[
|
|
'ref_type' => 'PRODUCT',
|
|
'ref_id' => Product::where('tenant_id', $tenant->id)->where('code', 'PIPE_KSS01_ROLLER')->first()->id,
|
|
'quantity' => 1,
|
|
'waste_rate' => 0,
|
|
'order' => 4
|
|
],
|
|
[
|
|
'ref_type' => 'MATERIAL',
|
|
'ref_id' => Material::where('tenant_id', $tenant->id)->where('code', 'CABLE_KSS01_LIFT')->first()->id,
|
|
'quantity' => 2, // Two cables
|
|
'waste_rate' => 10,
|
|
'order' => 5
|
|
]
|
|
];
|
|
|
|
foreach ($baseItems as $itemData) {
|
|
BomTemplateItem::firstOrCreate(
|
|
[
|
|
'bom_template_id' => $bomTemplate->id,
|
|
'ref_type' => $itemData['ref_type'],
|
|
'ref_id' => $itemData['ref_id'],
|
|
'order' => $itemData['order']
|
|
],
|
|
$itemData
|
|
);
|
|
}
|
|
}
|
|
}
|