Revert "feat: DB 연결 오버라이딩 및 대시보드 통계 위젯 추가"
This reverts commit bf8036a64b.
This commit is contained in:
@@ -1,167 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\BomConditionRule;
|
||||
use App\Models\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\BomConditionRule>
|
||||
*/
|
||||
class BomConditionRuleFactory extends Factory
|
||||
{
|
||||
protected $model = BomConditionRule::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'tenant_id' => 1,
|
||||
'model_id' => Model::factory(),
|
||||
'name' => $this->faker->words(3, true),
|
||||
'description' => $this->faker->sentence(),
|
||||
'condition_expression' => 'area > 5',
|
||||
'component_code' => 'BRK-001',
|
||||
'quantity_expression' => '2',
|
||||
'priority' => $this->faker->numberBetween(1, 100),
|
||||
'is_active' => true,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen model condition rules.
|
||||
*/
|
||||
public function screenRules(): static
|
||||
{
|
||||
return $this->sequence(
|
||||
[
|
||||
'name' => '케이스 규칙',
|
||||
'description' => '크기에 따른 케이스 선택',
|
||||
'condition_expression' => 'area <= 3',
|
||||
'component_code' => 'CASE-SMALL',
|
||||
'quantity_expression' => '1',
|
||||
'priority' => 10,
|
||||
],
|
||||
[
|
||||
'name' => '케이스 규칙 (중형)',
|
||||
'description' => '중형 크기 케이스',
|
||||
'condition_expression' => 'area > 3 AND area <= 6',
|
||||
'component_code' => 'CASE-MEDIUM',
|
||||
'quantity_expression' => '1',
|
||||
'priority' => 11,
|
||||
],
|
||||
[
|
||||
'name' => '케이스 규칙 (대형)',
|
||||
'description' => '대형 크기 케이스',
|
||||
'condition_expression' => 'area > 6',
|
||||
'component_code' => 'CASE-LARGE',
|
||||
'quantity_expression' => '1',
|
||||
'priority' => 12,
|
||||
],
|
||||
[
|
||||
'name' => '바텀 규칙',
|
||||
'description' => '바텀 개수',
|
||||
'condition_expression' => 'TRUE',
|
||||
'component_code' => 'BOTTOM-001',
|
||||
'quantity_expression' => 'CEIL(W1 / 1000)',
|
||||
'priority' => 20,
|
||||
],
|
||||
[
|
||||
'name' => '샤프트 규칙',
|
||||
'description' => '샤프트 길이',
|
||||
'condition_expression' => 'TRUE',
|
||||
'component_code' => 'SHAFT-001',
|
||||
'quantity_expression' => 'W1 / 1000',
|
||||
'priority' => 30,
|
||||
],
|
||||
[
|
||||
'name' => '파이프 규칙',
|
||||
'description' => '파이프 길이',
|
||||
'condition_expression' => 'screen_type = "SCREEN"',
|
||||
'component_code' => 'PIPE-SCREEN',
|
||||
'quantity_expression' => 'W1 / 1000',
|
||||
'priority' => 40,
|
||||
],
|
||||
[
|
||||
'name' => '슬라트 파이프 규칙',
|
||||
'description' => '슬라트용 파이프',
|
||||
'condition_expression' => 'screen_type = "SLAT"',
|
||||
'component_code' => 'PIPE-SLAT',
|
||||
'quantity_expression' => 'W1 / 1000',
|
||||
'priority' => 41,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steel model condition rules.
|
||||
*/
|
||||
public function steelRules(): static
|
||||
{
|
||||
return $this->sequence(
|
||||
[
|
||||
'name' => '프레임 규칙',
|
||||
'description' => '프레임 길이',
|
||||
'condition_expression' => 'TRUE',
|
||||
'component_code' => 'FRAME-STEEL',
|
||||
'quantity_expression' => '(W1 + H1) * 2 / 1000',
|
||||
'priority' => 10,
|
||||
],
|
||||
[
|
||||
'name' => '패널 규칙',
|
||||
'description' => '패널 개수',
|
||||
'condition_expression' => 'TRUE',
|
||||
'component_code' => 'PANEL-STEEL',
|
||||
'quantity_expression' => 'CEIL(area)',
|
||||
'priority' => 20,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* High priority rule.
|
||||
*/
|
||||
public function highPriority(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'priority' => $this->faker->numberBetween(1, 10),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Low priority rule.
|
||||
*/
|
||||
public function lowPriority(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'priority' => $this->faker->numberBetween(90, 100),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Active rule.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactive rule.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Model>
|
||||
*/
|
||||
class ModelFactory extends Factory
|
||||
{
|
||||
protected $model = Model::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'tenant_id' => 1,
|
||||
'code' => 'KSS' . $this->faker->numberBetween(10, 99),
|
||||
'name' => $this->faker->words(3, true),
|
||||
'description' => $this->faker->sentence(),
|
||||
'status' => $this->faker->randomElement(['DRAFT', 'RELEASED', 'ARCHIVED']),
|
||||
'product_family' => $this->faker->randomElement(['SCREEN', 'STEEL']),
|
||||
'is_active' => true,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model is active.
|
||||
*/
|
||||
public function active(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model is inactive.
|
||||
*/
|
||||
public function inactive(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'is_active' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model is released.
|
||||
*/
|
||||
public function released(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'status' => 'RELEASED',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model is draft.
|
||||
*/
|
||||
public function draft(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'status' => 'DRAFT',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen type model.
|
||||
*/
|
||||
public function screen(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'product_family' => 'SCREEN',
|
||||
'code' => 'KSS' . $this->faker->numberBetween(10, 99),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steel type model.
|
||||
*/
|
||||
public function steel(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'product_family' => 'STEEL',
|
||||
'code' => 'KST' . $this->faker->numberBetween(10, 99),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Models\ModelFormula;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ModelFormula>
|
||||
*/
|
||||
class ModelFormulaFactory extends Factory
|
||||
{
|
||||
protected $model = ModelFormula::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'tenant_id' => 1,
|
||||
'model_id' => Model::factory(),
|
||||
'name' => $this->faker->word(),
|
||||
'expression' => 'W0 * H0',
|
||||
'description' => $this->faker->sentence(),
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => $this->faker->numberBetween(1, 10),
|
||||
'is_active' => true,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen model formulas.
|
||||
*/
|
||||
public function screenFormulas(): static
|
||||
{
|
||||
return $this->sequence(
|
||||
[
|
||||
'name' => 'W1',
|
||||
'expression' => 'W0 + 120',
|
||||
'description' => '최종 가로 크기',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'H1',
|
||||
'expression' => 'H0 + 100',
|
||||
'description' => '최종 세로 크기',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 2,
|
||||
],
|
||||
[
|
||||
'name' => 'area',
|
||||
'expression' => 'W1 * H1 / 1000000',
|
||||
'description' => '면적 (㎡)',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 3,
|
||||
],
|
||||
[
|
||||
'name' => 'weight',
|
||||
'expression' => 'area * 15',
|
||||
'description' => '중량 (kg)',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 4,
|
||||
],
|
||||
[
|
||||
'name' => 'motor',
|
||||
'expression' => 'IF(area <= 3, "0.5HP", IF(area <= 6, "1HP", "2HP"))',
|
||||
'description' => '모터 용량',
|
||||
'return_type' => 'STRING',
|
||||
'sort_order' => 5,
|
||||
],
|
||||
[
|
||||
'name' => 'bracket',
|
||||
'expression' => 'CEIL(W1 / 600)',
|
||||
'description' => '브라켓 개수',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 6,
|
||||
],
|
||||
[
|
||||
'name' => 'guide',
|
||||
'expression' => 'H1 / 1000 * 2',
|
||||
'description' => '가이드 길이 (m)',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 7,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steel model formulas.
|
||||
*/
|
||||
public function steelFormulas(): static
|
||||
{
|
||||
return $this->sequence(
|
||||
[
|
||||
'name' => 'W1',
|
||||
'expression' => 'W0 + 50',
|
||||
'description' => '최종 가로 크기',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'H1',
|
||||
'expression' => 'H0 + 50',
|
||||
'description' => '최종 세로 크기',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 2,
|
||||
],
|
||||
[
|
||||
'name' => 'area',
|
||||
'expression' => 'W1 * H1 / 1000000',
|
||||
'description' => '면적 (㎡)',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 3,
|
||||
],
|
||||
[
|
||||
'name' => 'weight',
|
||||
'expression' => 'area * thickness * 7.85',
|
||||
'description' => '중량 (kg)',
|
||||
'return_type' => 'NUMBER',
|
||||
'sort_order' => 4,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number type formula.
|
||||
*/
|
||||
public function number(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'return_type' => 'NUMBER',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* String type formula.
|
||||
*/
|
||||
public function string(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'return_type' => 'STRING',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Model;
|
||||
use App\Models\ModelParameter;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ModelParameter>
|
||||
*/
|
||||
class ModelParameterFactory extends Factory
|
||||
{
|
||||
protected $model = ModelParameter::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'tenant_id' => 1,
|
||||
'model_id' => Model::factory(),
|
||||
'name' => $this->faker->word(),
|
||||
'label' => $this->faker->words(2, true),
|
||||
'type' => $this->faker->randomElement(['NUMBER', 'SELECT', 'BOOLEAN']),
|
||||
'default_value' => '0',
|
||||
'validation_rules' => json_encode(['required' => true]),
|
||||
'options' => null,
|
||||
'sort_order' => $this->faker->numberBetween(1, 10),
|
||||
'is_required' => true,
|
||||
'is_active' => true,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen model parameters.
|
||||
*/
|
||||
public function screenParameters(): static
|
||||
{
|
||||
return $this->sequence(
|
||||
[
|
||||
'name' => 'W0',
|
||||
'label' => '가로(mm)',
|
||||
'type' => 'NUMBER',
|
||||
'default_value' => '1000',
|
||||
'validation_rules' => json_encode(['required' => true, 'numeric' => true, 'min' => 500, 'max' => 3000]),
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'H0',
|
||||
'label' => '세로(mm)',
|
||||
'type' => 'NUMBER',
|
||||
'default_value' => '800',
|
||||
'validation_rules' => json_encode(['required' => true, 'numeric' => true, 'min' => 400, 'max' => 2000]),
|
||||
'sort_order' => 2,
|
||||
],
|
||||
[
|
||||
'name' => 'screen_type',
|
||||
'label' => '스크린 타입',
|
||||
'type' => 'SELECT',
|
||||
'default_value' => 'SCREEN',
|
||||
'validation_rules' => json_encode(['required' => true, 'in' => ['SCREEN', 'SLAT']]),
|
||||
'options' => json_encode([
|
||||
['value' => 'SCREEN', 'label' => '스크린'],
|
||||
['value' => 'SLAT', 'label' => '슬라트']
|
||||
]),
|
||||
'sort_order' => 3,
|
||||
],
|
||||
[
|
||||
'name' => 'install_type',
|
||||
'label' => '설치 방식',
|
||||
'type' => 'SELECT',
|
||||
'default_value' => 'WALL',
|
||||
'validation_rules' => json_encode(['required' => true, 'in' => ['WALL', 'SIDE', 'MIXED']]),
|
||||
'options' => json_encode([
|
||||
['value' => 'WALL', 'label' => '벽면 설치'],
|
||||
['value' => 'SIDE', 'label' => '측면 설치'],
|
||||
['value' => 'MIXED', 'label' => '혼합 설치']
|
||||
]),
|
||||
'sort_order' => 4,
|
||||
],
|
||||
[
|
||||
'name' => 'power_source',
|
||||
'label' => '전원',
|
||||
'type' => 'SELECT',
|
||||
'default_value' => 'AC',
|
||||
'validation_rules' => json_encode(['required' => true, 'in' => ['AC', 'DC', 'MANUAL']]),
|
||||
'options' => json_encode([
|
||||
['value' => 'AC', 'label' => 'AC 전원'],
|
||||
['value' => 'DC', 'label' => 'DC 전원'],
|
||||
['value' => 'MANUAL', 'label' => '수동']
|
||||
]),
|
||||
'sort_order' => 5,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steel model parameters.
|
||||
*/
|
||||
public function steelParameters(): static
|
||||
{
|
||||
return $this->sequence(
|
||||
[
|
||||
'name' => 'W0',
|
||||
'label' => '가로(mm)',
|
||||
'type' => 'NUMBER',
|
||||
'default_value' => '1200',
|
||||
'validation_rules' => json_encode(['required' => true, 'numeric' => true, 'min' => 800, 'max' => 4000]),
|
||||
'sort_order' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'H0',
|
||||
'label' => '세로(mm)',
|
||||
'type' => 'NUMBER',
|
||||
'default_value' => '1000',
|
||||
'validation_rules' => json_encode(['required' => true, 'numeric' => true, 'min' => 600, 'max' => 3000]),
|
||||
'sort_order' => 2,
|
||||
],
|
||||
[
|
||||
'name' => 'thickness',
|
||||
'label' => '두께(mm)',
|
||||
'type' => 'NUMBER',
|
||||
'default_value' => '50',
|
||||
'validation_rules' => json_encode(['required' => true, 'numeric' => true, 'min' => 20, 'max' => 100]),
|
||||
'sort_order' => 3,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number type parameter.
|
||||
*/
|
||||
public function number(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => 'NUMBER',
|
||||
'validation_rules' => json_encode(['required' => true, 'numeric' => true]),
|
||||
'options' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select type parameter.
|
||||
*/
|
||||
public function select(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => 'SELECT',
|
||||
'options' => json_encode([
|
||||
['value' => 'option1', 'label' => 'Option 1'],
|
||||
['value' => 'option2', 'label' => 'Option 2'],
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boolean type parameter.
|
||||
*/
|
||||
public function boolean(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'type' => 'BOOLEAN',
|
||||
'default_value' => 'false',
|
||||
'options' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('bom_template_groups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('model_id')->comment('모델 ID');
|
||||
$table->string('group_name', 100)->comment('그룹명 (본체, 절곡물, 가이드레일 등)');
|
||||
$table->unsignedBigInteger('parent_group_id')->nullable()->comment('상위 그룹 ID (계층 구조)');
|
||||
$table->integer('display_order')->default(0)->comment('표시 순서');
|
||||
$table->text('description')->nullable()->comment('그룹 설명');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||
|
||||
// 공통 컬럼
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'model_id']);
|
||||
$table->index(['tenant_id', 'model_id', 'parent_group_id']);
|
||||
$table->unique(['tenant_id', 'model_id', 'group_name'], 'unique_group_per_model');
|
||||
|
||||
// 외래키 (설계용, 프로덕션에서는 제거 고려)
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('model_id')->references('id')->on('models')->onDelete('cascade');
|
||||
$table->foreign('parent_group_id')->references('id')->on('bom_template_groups')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('bom_template_groups');
|
||||
}
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('model_parameters', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('model_id')->comment('모델 ID');
|
||||
$table->string('parameter_name', 50)->comment('매개변수명 (W0, H0, screen_type 등)');
|
||||
$table->enum('parameter_type', ['INTEGER', 'DECIMAL', 'STRING', 'BOOLEAN'])->comment('매개변수 타입');
|
||||
$table->boolean('is_required')->default(true)->comment('필수 여부');
|
||||
$table->string('default_value')->nullable()->comment('기본값');
|
||||
$table->decimal('min_value', 15, 4)->nullable()->comment('최소값 (숫자형)');
|
||||
$table->decimal('max_value', 15, 4)->nullable()->comment('최대값 (숫자형)');
|
||||
$table->json('allowed_values')->nullable()->comment('허용값 목록 (선택형)');
|
||||
$table->string('unit', 20)->nullable()->comment('단위 (mm, kg, 개 등)');
|
||||
$table->text('description')->nullable()->comment('매개변수 설명');
|
||||
$table->integer('display_order')->default(0)->comment('표시 순서');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||
|
||||
// 공통 컬럼
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'model_id']);
|
||||
$table->index(['tenant_id', 'model_id', 'display_order']);
|
||||
$table->unique(['tenant_id', 'model_id', 'parameter_name'], 'unique_parameter_per_model');
|
||||
|
||||
// 외래키 (설계용)
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('model_id')->references('id')->on('models')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('model_parameters');
|
||||
}
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('model_formulas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('model_id')->comment('모델 ID');
|
||||
$table->string('formula_name', 50)->comment('공식명 (W1, H1, area, weight 등)');
|
||||
$table->text('formula_expression')->comment('공식 표현식 (W0 + 100, W0 * H0 / 1000000 등)');
|
||||
$table->enum('result_type', ['INTEGER', 'DECIMAL'])->default('DECIMAL')->comment('결과 타입');
|
||||
$table->integer('decimal_places')->default(2)->comment('소수점 자릿수');
|
||||
$table->string('unit', 20)->nullable()->comment('결과 단위 (mm, kg, m² 등)');
|
||||
$table->text('description')->nullable()->comment('공식 설명');
|
||||
$table->integer('calculation_order')->default(0)->comment('계산 순서 (의존성 관리)');
|
||||
$table->json('dependencies')->nullable()->comment('의존 변수 목록');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||
|
||||
// 공통 컬럼
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'model_id']);
|
||||
$table->index(['tenant_id', 'model_id', 'calculation_order']);
|
||||
$table->unique(['tenant_id', 'model_id', 'formula_name'], 'unique_formula_per_model');
|
||||
|
||||
// 외래키 (설계용)
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('model_id')->references('id')->on('models')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('model_formulas');
|
||||
}
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('bom_condition_rules', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('model_id')->comment('모델 ID');
|
||||
$table->string('rule_name', 100)->comment('규칙명');
|
||||
$table->text('condition_expression')->comment('조건 표현식 (screen_type == "SCREEN", W0 > 1000 등)');
|
||||
$table->enum('action_type', ['INCLUDE_PRODUCT', 'EXCLUDE_PRODUCT', 'SET_QUANTITY', 'MODIFY_QUANTITY'])->comment('액션 타입');
|
||||
$table->unsignedBigInteger('target_product_id')->nullable()->comment('대상 제품 ID');
|
||||
$table->unsignedBigInteger('target_group_id')->nullable()->comment('대상 그룹 ID');
|
||||
$table->text('quantity_formula')->nullable()->comment('수량 공식 (ceiling(W0/1000), 2 * count 등)');
|
||||
$table->decimal('fixed_quantity', 15, 4)->nullable()->comment('고정 수량');
|
||||
$table->text('description')->nullable()->comment('규칙 설명');
|
||||
$table->integer('priority')->default(100)->comment('실행 우선순위 (낮을수록 먼저 실행)');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||
|
||||
// 공통 컬럼
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'model_id']);
|
||||
$table->index(['tenant_id', 'model_id', 'priority']);
|
||||
$table->index(['tenant_id', 'target_product_id']);
|
||||
$table->index(['tenant_id', 'target_group_id']);
|
||||
|
||||
// 외래키 (설계용)
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('model_id')->references('id')->on('models')->onDelete('cascade');
|
||||
$table->foreign('target_product_id')->references('id')->on('products')->onDelete('cascade');
|
||||
$table->foreign('target_group_id')->references('id')->on('bom_template_groups')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('bom_condition_rules');
|
||||
}
|
||||
};
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('product_parameters', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('model_id')->comment('모델 ID');
|
||||
$table->unsignedBigInteger('model_version_id')->nullable()->comment('모델 버전 ID');
|
||||
$table->string('product_code', 100)->nullable()->comment('제품 코드 (참조용)');
|
||||
$table->json('parameter_values')->comment('매개변수 값들 {W0: 1200, H0: 800, screen_type: "SCREEN"}');
|
||||
$table->text('notes')->nullable()->comment('비고');
|
||||
$table->enum('status', ['DRAFT', 'CALCULATED', 'APPROVED'])->default('DRAFT')->comment('상태');
|
||||
|
||||
// 공통 컬럼
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'model_id']);
|
||||
$table->index(['tenant_id', 'model_id', 'model_version_id']);
|
||||
$table->index(['tenant_id', 'product_code']);
|
||||
$table->index(['tenant_id', 'status']);
|
||||
$table->index(['created_at']);
|
||||
|
||||
// 외래키 (설계용)
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('model_id')->references('id')->on('models')->onDelete('cascade');
|
||||
$table->foreign('model_version_id')->references('id')->on('model_versions')->onDelete('set null');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('product_parameters');
|
||||
}
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('product_calculated_values', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('product_parameter_id')->comment('제품 매개변수 ID');
|
||||
$table->json('calculated_values')->comment('계산된 값들 {W1: 1300, H1: 900, area: 1.17, weight: 45.2}');
|
||||
$table->json('bom_snapshot')->nullable()->comment('계산된 BOM 결과 스냅샷');
|
||||
$table->decimal('total_cost', 15, 4)->nullable()->comment('총 비용');
|
||||
$table->decimal('total_weight', 15, 4)->nullable()->comment('총 중량');
|
||||
$table->integer('total_items')->nullable()->comment('총 아이템 수');
|
||||
$table->timestamp('calculation_date')->useCurrent()->comment('계산 일시');
|
||||
$table->boolean('is_valid')->default(true)->comment('계산 유효성 여부');
|
||||
$table->text('calculation_errors')->nullable()->comment('계산 오류 메시지');
|
||||
$table->string('calculation_version', 50)->nullable()->comment('계산 엔진 버전');
|
||||
|
||||
// 공통 컬럼
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'product_parameter_id']);
|
||||
$table->index(['tenant_id', 'is_valid']);
|
||||
$table->index(['calculation_date']);
|
||||
$table->unique(['tenant_id', 'product_parameter_id'], 'unique_calculation_per_parameter');
|
||||
|
||||
// 외래키 (설계용)
|
||||
$table->foreign('tenant_id')->references('id')->on('tenants')->onDelete('cascade');
|
||||
$table->foreign('product_parameter_id')->references('id')->on('product_parameters')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('product_calculated_values');
|
||||
}
|
||||
};
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('bom_template_items', function (Blueprint $table) {
|
||||
// group_id가 이미 존재하는지 확인 후 추가
|
||||
if (!Schema::hasColumn('bom_template_items', 'group_id')) {
|
||||
$table->unsignedBigInteger('group_id')->nullable()->after('bom_template_id')->comment('BOM 그룹 ID');
|
||||
}
|
||||
|
||||
// 나머지 컬럼들 추가
|
||||
if (!Schema::hasColumn('bom_template_items', 'is_conditional')) {
|
||||
$table->boolean('is_conditional')->default(false)->after('qty')->comment('조건부 아이템 여부');
|
||||
}
|
||||
if (!Schema::hasColumn('bom_template_items', 'condition_expression')) {
|
||||
$table->text('condition_expression')->nullable()->after('is_conditional')->comment('조건 표현식');
|
||||
}
|
||||
if (!Schema::hasColumn('bom_template_items', 'quantity_formula')) {
|
||||
$table->text('quantity_formula')->nullable()->after('condition_expression')->comment('수량 계산 공식');
|
||||
}
|
||||
});
|
||||
|
||||
// 인덱스와 외래키는 별도로 처리
|
||||
Schema::table('bom_template_items', function (Blueprint $table) {
|
||||
if (!Schema::hasIndex('bom_template_items', ['tenant_id', 'group_id'])) {
|
||||
$table->index(['tenant_id', 'group_id']);
|
||||
}
|
||||
// 외래키 제약조건은 생산 환경에서 제거 (SAM 규칙)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bom_template_items', function (Blueprint $table) {
|
||||
$table->dropForeign(['group_id']);
|
||||
$table->dropIndex(['tenant_id', 'group_id']);
|
||||
$table->dropColumn(['group_id', 'is_conditional', 'condition_expression', 'quantity_formula']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->boolean('is_parametric')->default(false)->after('product_type')->comment('매개변수 기반 제품 여부');
|
||||
$table->unsignedBigInteger('base_model_id')->nullable()->after('is_parametric')->comment('기반 모델 ID (매개변수 제품용)');
|
||||
$table->json('parameter_values')->nullable()->after('base_model_id')->comment('매개변수 값들 (매개변수 제품용)');
|
||||
$table->json('calculated_values')->nullable()->after('parameter_values')->comment('계산된 값들 (매개변수 제품용)');
|
||||
|
||||
// 인덱스 추가
|
||||
$table->index(['tenant_id', 'is_parametric']);
|
||||
$table->index(['tenant_id', 'base_model_id']);
|
||||
|
||||
// 외래키 제약조건은 생산 환경에서 제거 (SAM 규칙)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->dropForeign(['base_model_id']);
|
||||
$table->dropIndex(['tenant_id', 'is_parametric']);
|
||||
$table->dropIndex(['tenant_id', 'base_model_id']);
|
||||
$table->dropColumn(['is_parametric', 'base_model_id', 'parameter_values', 'calculated_values']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,573 +0,0 @@
|
||||
<?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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\BomConditionRule;
|
||||
use App\Models\Model;
|
||||
use App\Models\ModelFormula;
|
||||
use App\Models\ModelParameter;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ParameterBasedBomTestSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Create KSS01 (Screen) Model with complete parameter-based BOM setup
|
||||
$kss01 = Model::factory()
|
||||
->screen()
|
||||
->released()
|
||||
->create([
|
||||
'code' => 'KSS01',
|
||||
'name' => '스크린 블라인드 표준형',
|
||||
'description' => '매개변수 기반 스크린 블라인드',
|
||||
'product_family' => 'SCREEN',
|
||||
]);
|
||||
|
||||
// Create parameters for KSS01
|
||||
ModelParameter::factory()
|
||||
->screenParameters()
|
||||
->create(['model_id' => $kss01->id]);
|
||||
|
||||
// Create formulas for KSS01
|
||||
ModelFormula::factory()
|
||||
->screenFormulas()
|
||||
->create(['model_id' => $kss01->id]);
|
||||
|
||||
// Create condition rules for KSS01
|
||||
BomConditionRule::factory()
|
||||
->screenRules()
|
||||
->create(['model_id' => $kss01->id]);
|
||||
|
||||
// Create KST01 (Steel) Model
|
||||
$kst01 = Model::factory()
|
||||
->steel()
|
||||
->released()
|
||||
->create([
|
||||
'code' => 'KST01',
|
||||
'name' => '스틸 도어 표준형',
|
||||
'description' => '매개변수 기반 스틸 도어',
|
||||
'product_family' => 'STEEL',
|
||||
]);
|
||||
|
||||
// Create parameters for KST01
|
||||
ModelParameter::factory()
|
||||
->steelParameters()
|
||||
->create(['model_id' => $kst01->id]);
|
||||
|
||||
// Create formulas for KST01
|
||||
ModelFormula::factory()
|
||||
->steelFormulas()
|
||||
->create(['model_id' => $kst01->id]);
|
||||
|
||||
// Create condition rules for KST01
|
||||
BomConditionRule::factory()
|
||||
->steelRules()
|
||||
->create(['model_id' => $kst01->id]);
|
||||
|
||||
// Create additional test models for edge cases
|
||||
$testModels = [
|
||||
// Draft model for testing incomplete states
|
||||
[
|
||||
'code' => 'TEST-DRAFT',
|
||||
'name' => '테스트 드래프트 모델',
|
||||
'status' => 'DRAFT',
|
||||
'product_family' => 'SCREEN',
|
||||
],
|
||||
// Inactive model for testing filtering
|
||||
[
|
||||
'code' => 'TEST-INACTIVE',
|
||||
'name' => '테스트 비활성 모델',
|
||||
'status' => 'RELEASED',
|
||||
'product_family' => 'SCREEN',
|
||||
'is_active' => false,
|
||||
],
|
||||
// Complex model for performance testing
|
||||
[
|
||||
'code' => 'COMPLEX-01',
|
||||
'name' => '복합 테스트 모델',
|
||||
'status' => 'RELEASED',
|
||||
'product_family' => 'SCREEN',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($testModels as $modelData) {
|
||||
$model = Model::factory()->create($modelData);
|
||||
|
||||
// Add basic parameters
|
||||
ModelParameter::factory()
|
||||
->count(3)
|
||||
->number()
|
||||
->create(['model_id' => $model->id]);
|
||||
|
||||
// Add basic formulas
|
||||
ModelFormula::factory()
|
||||
->count(2)
|
||||
->number()
|
||||
->create(['model_id' => $model->id]);
|
||||
|
||||
// Add condition rules
|
||||
BomConditionRule::factory()
|
||||
->count(2)
|
||||
->create(['model_id' => $model->id]);
|
||||
}
|
||||
|
||||
// Create models for multi-tenant testing
|
||||
$tenant2Models = Model::factory()
|
||||
->count(3)
|
||||
->screen()
|
||||
->create(['tenant_id' => 2]);
|
||||
|
||||
foreach ($tenant2Models as $model) {
|
||||
ModelParameter::factory()
|
||||
->count(2)
|
||||
->create(['model_id' => $model->id, 'tenant_id' => 2]);
|
||||
|
||||
ModelFormula::factory()
|
||||
->count(1)
|
||||
->create(['model_id' => $model->id, 'tenant_id' => 2]);
|
||||
}
|
||||
|
||||
// Create performance test data (large dataset)
|
||||
if (app()->environment('testing')) {
|
||||
$performanceModel = Model::factory()
|
||||
->screen()
|
||||
->create([
|
||||
'code' => 'PERF-TEST',
|
||||
'name' => '성능 테스트 모델',
|
||||
]);
|
||||
|
||||
// Large number of parameters for performance testing
|
||||
ModelParameter::factory()
|
||||
->count(50)
|
||||
->create(['model_id' => $performanceModel->id]);
|
||||
|
||||
// Large number of formulas
|
||||
ModelFormula::factory()
|
||||
->count(30)
|
||||
->create(['model_id' => $performanceModel->id]);
|
||||
|
||||
// Large number of condition rules
|
||||
BomConditionRule::factory()
|
||||
->count(100)
|
||||
->create(['model_id' => $performanceModel->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,820 +0,0 @@
|
||||
<?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 ParametricBomSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds for parametric BOM testing.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Create test tenant
|
||||
$tenant = Tenant::firstOrCreate(
|
||||
['code' => 'TEST_TENANT'],
|
||||
[
|
||||
'name' => 'Test Tenant for Parametric BOM',
|
||||
'description' => 'Tenant for testing parametric BOM system',
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
// Create test user
|
||||
$user = User::firstOrCreate(
|
||||
['email' => 'test@parametric-bom.com'],
|
||||
[
|
||||
'name' => 'Parametric BOM Test User',
|
||||
'password' => Hash::make('password'),
|
||||
'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 categories
|
||||
$screenCategory = Category::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => 'SCREEN_SYSTEMS'
|
||||
],
|
||||
[
|
||||
'name' => 'Screen Door Systems',
|
||||
'description' => 'Automatic screen door systems',
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
$materialsCategory = Category::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => 'MATERIALS'
|
||||
],
|
||||
[
|
||||
'name' => 'Raw Materials',
|
||||
'description' => 'Raw materials and components',
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
$componentsCategory = Category::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => 'COMPONENTS'
|
||||
],
|
||||
[
|
||||
'name' => 'Components',
|
||||
'description' => 'Manufactured components and parts',
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
// Create test products
|
||||
$this->createTestProducts($tenant, $componentsCategory);
|
||||
|
||||
// Create test materials
|
||||
$this->createTestMaterials($tenant, $materialsCategory);
|
||||
|
||||
// Create design models
|
||||
$this->createDesignModels($tenant, $screenCategory);
|
||||
|
||||
$this->command->info('Parametric BOM test data seeded successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create test products
|
||||
*/
|
||||
private function createTestProducts(Tenant $tenant, Category $category): void
|
||||
{
|
||||
$products = [
|
||||
[
|
||||
'code' => 'BRACKET_WALL_STD',
|
||||
'name' => 'Standard Wall Bracket',
|
||||
'description' => 'Standard mounting bracket for wall installation',
|
||||
'unit' => 'EA',
|
||||
'weight' => 0.5,
|
||||
'color' => 'WHITE'
|
||||
],
|
||||
[
|
||||
'code' => 'BRACKET_CEILING_STD',
|
||||
'name' => 'Standard Ceiling Bracket',
|
||||
'description' => 'Standard mounting bracket for ceiling installation',
|
||||
'unit' => 'EA',
|
||||
'weight' => 0.7,
|
||||
'color' => 'WHITE'
|
||||
],
|
||||
[
|
||||
'code' => 'MOTOR_DC_12V',
|
||||
'name' => 'DC Motor 12V',
|
||||
'description' => 'DC motor for screen operation, 12V',
|
||||
'unit' => 'EA',
|
||||
'weight' => 2.5,
|
||||
'color' => 'BLACK'
|
||||
],
|
||||
[
|
||||
'code' => 'MOTOR_DC_24V',
|
||||
'name' => 'DC Motor 24V',
|
||||
'description' => 'DC motor for screen operation, 24V (for larger screens)',
|
||||
'unit' => 'EA',
|
||||
'weight' => 3.2,
|
||||
'color' => 'BLACK'
|
||||
],
|
||||
[
|
||||
'code' => 'CONTROLLER_BASIC',
|
||||
'name' => 'Basic Controller',
|
||||
'description' => 'Basic remote controller for screen operation',
|
||||
'unit' => 'EA',
|
||||
'weight' => 0.2,
|
||||
'color' => 'WHITE'
|
||||
],
|
||||
[
|
||||
'code' => 'CONTROLLER_SMART',
|
||||
'name' => 'Smart Controller',
|
||||
'description' => 'Smart controller with app connectivity',
|
||||
'unit' => 'EA',
|
||||
'weight' => 0.3,
|
||||
'color' => 'BLACK'
|
||||
],
|
||||
[
|
||||
'code' => 'CASE_ALUMINUM',
|
||||
'name' => 'Aluminum Case',
|
||||
'description' => 'Aluminum housing case for screen mechanism',
|
||||
'unit' => 'EA',
|
||||
'weight' => 1.8,
|
||||
'color' => 'SILVER'
|
||||
],
|
||||
[
|
||||
'code' => 'CASE_PLASTIC',
|
||||
'name' => 'Plastic Case',
|
||||
'description' => 'Plastic housing case for screen mechanism',
|
||||
'unit' => 'EA',
|
||||
'weight' => 1.2,
|
||||
'color' => 'WHITE'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($products as $productData) {
|
||||
Product::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => $productData['code']
|
||||
],
|
||||
array_merge($productData, [
|
||||
'category_id' => $category->id,
|
||||
'is_active' => true,
|
||||
'created_by' => 1
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create test materials
|
||||
*/
|
||||
private function createTestMaterials(Tenant $tenant, Category $category): void
|
||||
{
|
||||
$materials = [
|
||||
[
|
||||
'code' => 'FABRIC_SCREEN_STD',
|
||||
'name' => 'Standard Screen Fabric',
|
||||
'description' => 'Standard fabric material for screen doors',
|
||||
'unit' => 'M2',
|
||||
'density' => 0.3, // kg/m2
|
||||
'color' => 'GRAY'
|
||||
],
|
||||
[
|
||||
'code' => 'FABRIC_SCREEN_PREMIUM',
|
||||
'name' => 'Premium Screen Fabric',
|
||||
'description' => 'Premium fabric material with enhanced durability',
|
||||
'unit' => 'M2',
|
||||
'density' => 0.4,
|
||||
'color' => 'CHARCOAL'
|
||||
],
|
||||
[
|
||||
'code' => 'STEEL_MESH_FINE',
|
||||
'name' => 'Fine Steel Mesh',
|
||||
'description' => 'Fine steel mesh for security screens',
|
||||
'unit' => 'M2',
|
||||
'density' => 2.5,
|
||||
'color' => 'SILVER'
|
||||
],
|
||||
[
|
||||
'code' => 'STEEL_MESH_COARSE',
|
||||
'name' => 'Coarse Steel Mesh',
|
||||
'description' => 'Coarse steel mesh for ventilation screens',
|
||||
'unit' => 'M2',
|
||||
'density' => 2.0,
|
||||
'color' => 'SILVER'
|
||||
],
|
||||
[
|
||||
'code' => 'RAIL_GUIDE_ALU',
|
||||
'name' => 'Aluminum Guide Rail',
|
||||
'description' => 'Aluminum guide rail for screen movement',
|
||||
'unit' => 'M',
|
||||
'density' => 0.8, // kg/m
|
||||
'color' => 'SILVER'
|
||||
],
|
||||
[
|
||||
'code' => 'RAIL_GUIDE_PLASTIC',
|
||||
'name' => 'Plastic Guide Rail',
|
||||
'description' => 'Plastic guide rail for lightweight screens',
|
||||
'unit' => 'M',
|
||||
'density' => 0.3,
|
||||
'color' => 'WHITE'
|
||||
],
|
||||
[
|
||||
'code' => 'CABLE_STEEL',
|
||||
'name' => 'Steel Cable',
|
||||
'description' => 'Steel cable for screen lifting mechanism',
|
||||
'unit' => 'M',
|
||||
'density' => 0.1,
|
||||
'color' => 'SILVER'
|
||||
],
|
||||
[
|
||||
'code' => 'SPRING_TENSION',
|
||||
'name' => 'Tension Spring',
|
||||
'description' => 'Spring for screen tension adjustment',
|
||||
'unit' => 'EA',
|
||||
'weight' => 0.05,
|
||||
'color' => 'SILVER'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($materials as $materialData) {
|
||||
Material::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => $materialData['code']
|
||||
],
|
||||
array_merge($materialData, [
|
||||
'category_id' => $category->id,
|
||||
'is_active' => true,
|
||||
'created_by' => 1
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create design models with parameters, formulas, and rules
|
||||
*/
|
||||
private function createDesignModels(Tenant $tenant, Category $category): void
|
||||
{
|
||||
// Create basic screen door model
|
||||
$basicModel = DesignModel::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => 'BSD01'
|
||||
],
|
||||
[
|
||||
'name' => 'Basic Screen Door',
|
||||
'category_id' => $category->id,
|
||||
'lifecycle' => 'ACTIVE',
|
||||
'description' => 'Basic parametric screen door model for testing',
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
$this->createModelParameters($tenant, $basicModel);
|
||||
$this->createModelFormulas($tenant, $basicModel);
|
||||
$this->createBomConditionRules($tenant, $basicModel);
|
||||
$this->createBomTemplate($tenant, $basicModel);
|
||||
|
||||
// Create advanced screen door model
|
||||
$advancedModel = DesignModel::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'code' => 'ASD01'
|
||||
],
|
||||
[
|
||||
'name' => 'Advanced Screen Door',
|
||||
'category_id' => $category->id,
|
||||
'lifecycle' => 'ACTIVE',
|
||||
'description' => 'Advanced parametric screen door model with more features',
|
||||
'is_active' => true
|
||||
]
|
||||
);
|
||||
|
||||
$this->createAdvancedModelParameters($tenant, $advancedModel);
|
||||
$this->createAdvancedModelFormulas($tenant, $advancedModel);
|
||||
$this->createAdvancedBomConditionRules($tenant, $advancedModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create basic model parameters
|
||||
*/
|
||||
private function createModelParameters(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
$parameters = [
|
||||
[
|
||||
'parameter_name' => 'width',
|
||||
'parameter_type' => 'NUMBER',
|
||||
'is_required' => true,
|
||||
'default_value' => '1000',
|
||||
'min_value' => 500,
|
||||
'max_value' => 3000,
|
||||
'unit' => 'mm',
|
||||
'description' => 'Screen width',
|
||||
'sort_order' => 1
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'height',
|
||||
'parameter_type' => 'NUMBER',
|
||||
'is_required' => true,
|
||||
'default_value' => '2000',
|
||||
'min_value' => 1000,
|
||||
'max_value' => 3000,
|
||||
'unit' => 'mm',
|
||||
'description' => 'Screen height',
|
||||
'sort_order' => 2
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'screen_type',
|
||||
'parameter_type' => 'SELECT',
|
||||
'is_required' => true,
|
||||
'default_value' => 'FABRIC',
|
||||
'options' => ['FABRIC', 'STEEL_FINE', 'STEEL_COARSE'],
|
||||
'description' => 'Type of screen material',
|
||||
'sort_order' => 3
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'installation_type',
|
||||
'parameter_type' => 'SELECT',
|
||||
'is_required' => true,
|
||||
'default_value' => 'WALL',
|
||||
'options' => ['WALL', 'CEILING', 'RECESSED'],
|
||||
'description' => 'Installation method',
|
||||
'sort_order' => 4
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'motor_power',
|
||||
'parameter_type' => 'SELECT',
|
||||
'is_required' => false,
|
||||
'default_value' => 'AUTO',
|
||||
'options' => ['AUTO', '12V', '24V'],
|
||||
'description' => 'Motor power selection (AUTO = based on size)',
|
||||
'sort_order' => 5
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($parameters as $paramData) {
|
||||
ModelParameter::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'parameter_name' => $paramData['parameter_name']
|
||||
],
|
||||
$paramData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create basic model formulas
|
||||
*/
|
||||
private function createModelFormulas(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
$formulas = [
|
||||
[
|
||||
'formula_name' => 'outer_width',
|
||||
'expression' => 'width + 100',
|
||||
'description' => 'Outer width including frame',
|
||||
'sort_order' => 1
|
||||
],
|
||||
[
|
||||
'formula_name' => 'outer_height',
|
||||
'expression' => 'height + 150',
|
||||
'description' => 'Outer height including frame',
|
||||
'sort_order' => 2
|
||||
],
|
||||
[
|
||||
'formula_name' => 'screen_area',
|
||||
'expression' => '(width * height) / 1000000',
|
||||
'description' => 'Screen area in square meters',
|
||||
'sort_order' => 3
|
||||
],
|
||||
[
|
||||
'formula_name' => 'frame_perimeter',
|
||||
'expression' => '(outer_width + outer_height) * 2 / 1000',
|
||||
'description' => 'Frame perimeter in meters',
|
||||
'sort_order' => 4
|
||||
],
|
||||
[
|
||||
'formula_name' => 'total_weight',
|
||||
'expression' => 'screen_area * 0.5 + frame_perimeter * 0.8',
|
||||
'description' => 'Estimated total weight in kg',
|
||||
'sort_order' => 5
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($formulas as $formulaData) {
|
||||
ModelFormula::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'formula_name' => $formulaData['formula_name']
|
||||
],
|
||||
$formulaData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create BOM condition rules for basic model
|
||||
*/
|
||||
private function createBomConditionRules(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
$rules = [
|
||||
// Motor selection based on size
|
||||
[
|
||||
'rule_name' => 'Large Screen Motor',
|
||||
'condition_expression' => 'screen_area > 4.0 OR motor_power == "24V"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'MOTOR_DC_24V')->first()->id,
|
||||
'quantity_multiplier' => 1,
|
||||
'description' => 'Use 24V motor for large screens',
|
||||
'sort_order' => 1
|
||||
],
|
||||
[
|
||||
'rule_name' => 'Standard Screen Motor',
|
||||
'condition_expression' => 'screen_area <= 4.0 AND motor_power != "24V"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'MOTOR_DC_12V')->first()->id,
|
||||
'quantity_multiplier' => 1,
|
||||
'description' => 'Use 12V motor for standard screens',
|
||||
'sort_order' => 2
|
||||
],
|
||||
// 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_SCREEN_STD')->first()->id,
|
||||
'quantity_multiplier' => 1.1, // 10% waste
|
||||
'description' => 'Standard fabric screen material',
|
||||
'sort_order' => 3
|
||||
],
|
||||
[
|
||||
'rule_name' => 'Fine Steel Screen Material',
|
||||
'condition_expression' => 'screen_type == "STEEL_FINE"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'MATERIAL',
|
||||
'target_id' => Material::where('tenant_id', $tenant->id)->where('code', 'STEEL_MESH_FINE')->first()->id,
|
||||
'quantity_multiplier' => 1.05, // 5% waste
|
||||
'description' => 'Fine steel mesh material',
|
||||
'sort_order' => 4
|
||||
],
|
||||
// Bracket selection based on installation
|
||||
[
|
||||
'rule_name' => 'Wall Brackets',
|
||||
'condition_expression' => 'installation_type == "WALL"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'BRACKET_WALL_STD')->first()->id,
|
||||
'quantity_multiplier' => 2, // Always 2 brackets
|
||||
'description' => 'Wall mounting brackets',
|
||||
'sort_order' => 5
|
||||
],
|
||||
[
|
||||
'rule_name' => 'Ceiling Brackets',
|
||||
'condition_expression' => 'installation_type == "CEILING"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'BRACKET_CEILING_STD')->first()->id,
|
||||
'quantity_multiplier' => 2,
|
||||
'description' => 'Ceiling mounting brackets',
|
||||
'sort_order' => 6
|
||||
],
|
||||
// Extra brackets for wide screens
|
||||
[
|
||||
'rule_name' => 'Extra Brackets for Wide Screens',
|
||||
'condition_expression' => 'width > 2000',
|
||||
'action_type' => 'MODIFY_QUANTITY',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'BRACKET_WALL_STD')->first()->id,
|
||||
'quantity_multiplier' => 1.5, // Add 50% more brackets
|
||||
'description' => 'Additional brackets for wide screens',
|
||||
'sort_order' => 7
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($rules as $ruleData) {
|
||||
BomConditionRule::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'rule_name' => $ruleData['rule_name']
|
||||
],
|
||||
$ruleData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create BOM template for basic model
|
||||
*/
|
||||
private function createBomTemplate(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
// Create model version first
|
||||
$modelVersion = ModelVersion::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'version_no' => '1.0'
|
||||
],
|
||||
[
|
||||
'status' => 'RELEASED',
|
||||
'description' => 'Initial release version',
|
||||
'created_by' => 1
|
||||
]
|
||||
);
|
||||
|
||||
// Create BOM template
|
||||
$bomTemplate = BomTemplate::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_version_id' => $modelVersion->id,
|
||||
'name' => 'Basic Screen Door BOM'
|
||||
],
|
||||
[
|
||||
'description' => 'Base BOM template for basic screen door',
|
||||
'is_active' => true,
|
||||
'created_by' => 1
|
||||
]
|
||||
);
|
||||
|
||||
// Create BOM template items (base components always included)
|
||||
$baseItems = [
|
||||
[
|
||||
'ref_type' => 'MATERIAL',
|
||||
'ref_id' => Material::where('tenant_id', $tenant->id)->where('code', 'RAIL_GUIDE_ALU')->first()->id,
|
||||
'quantity' => 1, // Will be calculated by formula
|
||||
'waste_rate' => 5,
|
||||
'order' => 1
|
||||
],
|
||||
[
|
||||
'ref_type' => 'MATERIAL',
|
||||
'ref_id' => Material::where('tenant_id', $tenant->id)->where('code', 'CABLE_STEEL')->first()->id,
|
||||
'quantity' => 2, // Height * 2
|
||||
'waste_rate' => 10,
|
||||
'order' => 2
|
||||
],
|
||||
[
|
||||
'ref_type' => 'MATERIAL',
|
||||
'ref_id' => Material::where('tenant_id', $tenant->id)->where('code', 'SPRING_TENSION')->first()->id,
|
||||
'quantity' => 2,
|
||||
'waste_rate' => 0,
|
||||
'order' => 3
|
||||
],
|
||||
[
|
||||
'ref_type' => 'PRODUCT',
|
||||
'ref_id' => Product::where('tenant_id', $tenant->id)->where('code', 'CONTROLLER_BASIC')->first()->id,
|
||||
'quantity' => 1,
|
||||
'waste_rate' => 0,
|
||||
'order' => 4
|
||||
],
|
||||
[
|
||||
'ref_type' => 'PRODUCT',
|
||||
'ref_id' => Product::where('tenant_id', $tenant->id)->where('code', 'CASE_ALUMINUM')->first()->id,
|
||||
'quantity' => 1,
|
||||
'waste_rate' => 0,
|
||||
'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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create advanced model parameters
|
||||
*/
|
||||
private function createAdvancedModelParameters(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
$parameters = [
|
||||
[
|
||||
'parameter_name' => 'width',
|
||||
'parameter_type' => 'NUMBER',
|
||||
'is_required' => true,
|
||||
'default_value' => '1200',
|
||||
'min_value' => 600,
|
||||
'max_value' => 4000,
|
||||
'unit' => 'mm',
|
||||
'description' => 'Screen width',
|
||||
'sort_order' => 1
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'height',
|
||||
'parameter_type' => 'NUMBER',
|
||||
'is_required' => true,
|
||||
'default_value' => '2200',
|
||||
'min_value' => 1200,
|
||||
'max_value' => 3500,
|
||||
'unit' => 'mm',
|
||||
'description' => 'Screen height',
|
||||
'sort_order' => 2
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'screen_type',
|
||||
'parameter_type' => 'SELECT',
|
||||
'is_required' => true,
|
||||
'default_value' => 'FABRIC_PREMIUM',
|
||||
'options' => ['FABRIC_STD', 'FABRIC_PREMIUM', 'STEEL_FINE', 'STEEL_COARSE'],
|
||||
'description' => 'Type of screen material',
|
||||
'sort_order' => 3
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'controller_type',
|
||||
'parameter_type' => 'SELECT',
|
||||
'is_required' => true,
|
||||
'default_value' => 'SMART',
|
||||
'options' => ['BASIC', 'SMART'],
|
||||
'description' => 'Controller type',
|
||||
'sort_order' => 4
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'case_material',
|
||||
'parameter_type' => 'SELECT',
|
||||
'is_required' => true,
|
||||
'default_value' => 'ALUMINUM',
|
||||
'options' => ['ALUMINUM', 'PLASTIC'],
|
||||
'description' => 'Case material',
|
||||
'sort_order' => 5
|
||||
],
|
||||
[
|
||||
'parameter_name' => 'wind_resistance',
|
||||
'parameter_type' => 'NUMBER',
|
||||
'is_required' => false,
|
||||
'default_value' => '80',
|
||||
'min_value' => 40,
|
||||
'max_value' => 120,
|
||||
'unit' => 'km/h',
|
||||
'description' => 'Required wind resistance',
|
||||
'sort_order' => 6
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($parameters as $paramData) {
|
||||
ModelParameter::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'parameter_name' => $paramData['parameter_name']
|
||||
],
|
||||
$paramData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create advanced model formulas
|
||||
*/
|
||||
private function createAdvancedModelFormulas(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
$formulas = [
|
||||
[
|
||||
'formula_name' => 'outer_width',
|
||||
'expression' => 'width + 120',
|
||||
'description' => 'Outer width including reinforced frame',
|
||||
'sort_order' => 1
|
||||
],
|
||||
[
|
||||
'formula_name' => 'outer_height',
|
||||
'expression' => 'height + 180',
|
||||
'description' => 'Outer height including reinforced frame',
|
||||
'sort_order' => 2
|
||||
],
|
||||
[
|
||||
'formula_name' => 'screen_area',
|
||||
'expression' => '(width * height) / 1000000',
|
||||
'description' => 'Screen area in square meters',
|
||||
'sort_order' => 3
|
||||
],
|
||||
[
|
||||
'formula_name' => 'wind_load',
|
||||
'expression' => 'screen_area * wind_resistance * 0.6',
|
||||
'description' => 'Wind load calculation in Newtons',
|
||||
'sort_order' => 4
|
||||
],
|
||||
[
|
||||
'formula_name' => 'required_motor_torque',
|
||||
'expression' => 'wind_load * 0.1 + screen_area * 5',
|
||||
'description' => 'Required motor torque in Nm',
|
||||
'sort_order' => 5
|
||||
],
|
||||
[
|
||||
'formula_name' => 'frame_sections_count',
|
||||
'expression' => 'ceiling(width / 600) + ceiling(height / 800)',
|
||||
'description' => 'Number of frame sections needed',
|
||||
'sort_order' => 6
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($formulas as $formulaData) {
|
||||
ModelFormula::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'formula_name' => $formulaData['formula_name']
|
||||
],
|
||||
$formulaData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create advanced BOM condition rules
|
||||
*/
|
||||
private function createAdvancedBomConditionRules(Tenant $tenant, DesignModel $model): void
|
||||
{
|
||||
$rules = [
|
||||
// Motor selection based on calculated torque
|
||||
[
|
||||
'rule_name' => 'High Torque Motor Required',
|
||||
'condition_expression' => 'required_motor_torque > 15 OR wind_resistance > 100',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'MOTOR_DC_24V')->first()->id,
|
||||
'quantity_multiplier' => 1,
|
||||
'description' => 'Use high-power motor for demanding conditions',
|
||||
'sort_order' => 1
|
||||
],
|
||||
// Screen material selection
|
||||
[
|
||||
'rule_name' => 'Premium Fabric Material',
|
||||
'condition_expression' => 'screen_type == "FABRIC_PREMIUM"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'MATERIAL',
|
||||
'target_id' => Material::where('tenant_id', $tenant->id)->where('code', 'FABRIC_SCREEN_PREMIUM')->first()->id,
|
||||
'quantity_multiplier' => 1.05,
|
||||
'description' => 'Premium fabric screen material',
|
||||
'sort_order' => 2
|
||||
],
|
||||
// Controller selection
|
||||
[
|
||||
'rule_name' => 'Smart Controller',
|
||||
'condition_expression' => 'controller_type == "SMART"',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'CONTROLLER_SMART')->first()->id,
|
||||
'quantity_multiplier' => 1,
|
||||
'description' => 'Smart controller with app connectivity',
|
||||
'sort_order' => 3
|
||||
],
|
||||
// Case material selection
|
||||
[
|
||||
'rule_name' => 'Plastic Case for Light Duty',
|
||||
'condition_expression' => 'case_material == "PLASTIC" AND wind_resistance <= 60',
|
||||
'action_type' => 'INCLUDE',
|
||||
'target_type' => 'PRODUCT',
|
||||
'target_id' => Product::where('tenant_id', $tenant->id)->where('code', 'CASE_PLASTIC')->first()->id,
|
||||
'quantity_multiplier' => 1,
|
||||
'description' => 'Plastic case for light-duty applications',
|
||||
'sort_order' => 4
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($rules as $ruleData) {
|
||||
BomConditionRule::firstOrCreate(
|
||||
[
|
||||
'tenant_id' => $tenant->id,
|
||||
'model_id' => $model->id,
|
||||
'rule_name' => $ruleData['rule_name']
|
||||
],
|
||||
$ruleData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user