style: Laravel Pint 코드 포맷팅 적용
- PSR-12 스타일 가이드 준수 - 302개 파일 스타일 이슈 자동 수정 - 코드 로직 변경 없음 (포맷팅만)
This commit is contained in:
@@ -15,23 +15,23 @@ public function up(): void
|
||||
*/
|
||||
Schema::table('menus', function (Blueprint $table) {
|
||||
// slug 추가 (권한 키로 활용) - 테넌트 내 유니크
|
||||
if (!Schema::hasColumn('menus', 'slug')) {
|
||||
if (! Schema::hasColumn('menus', 'slug')) {
|
||||
$table->string('slug', 150)->nullable()->after('name')->comment('메뉴 슬러그(권한 키)');
|
||||
}
|
||||
|
||||
// Soft delete
|
||||
if (!Schema::hasColumn('menus', 'deleted_at')) {
|
||||
if (! Schema::hasColumn('menus', 'deleted_at')) {
|
||||
$table->softDeletes()->comment('소프트삭제 시각');
|
||||
}
|
||||
|
||||
// acted-by (누가 생성/수정/삭제 했는지)
|
||||
if (!Schema::hasColumn('menus', 'created_by')) {
|
||||
if (! Schema::hasColumn('menus', 'created_by')) {
|
||||
$table->unsignedBigInteger('created_by')->nullable()->after('updated_at')->comment('생성자 사용자 ID');
|
||||
}
|
||||
if (!Schema::hasColumn('menus', 'updated_by')) {
|
||||
if (! Schema::hasColumn('menus', 'updated_by')) {
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->after('created_by')->comment('수정자 사용자 ID');
|
||||
}
|
||||
if (!Schema::hasColumn('menus', 'deleted_by')) {
|
||||
if (! Schema::hasColumn('menus', 'deleted_by')) {
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->after('updated_by')->comment('삭제자 사용자 ID');
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public function up(): void
|
||||
/**
|
||||
* 2) 부서 테이블
|
||||
*/
|
||||
if (!Schema::hasTable('departments')) {
|
||||
if (! Schema::hasTable('departments')) {
|
||||
Schema::create('departments', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK: 부서 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -74,7 +74,7 @@ public function up(): void
|
||||
* 3) 부서-사용자 매핑
|
||||
* - 동일 부서-사용자 중복 매핑 방지(tenant_id 포함)
|
||||
*/
|
||||
if (!Schema::hasTable('department_user')) {
|
||||
if (! Schema::hasTable('department_user')) {
|
||||
Schema::create('department_user', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -101,7 +101,7 @@ public function up(): void
|
||||
* - ALLOW/DENY는 is_allowed로 표현
|
||||
* - 필요시 메뉴 단위 범위 제한을 위해 menu_id(옵션)
|
||||
*/
|
||||
if (!Schema::hasTable('department_permissions')) {
|
||||
if (! Schema::hasTable('department_permissions')) {
|
||||
Schema::create('department_permissions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -129,7 +129,7 @@ public function up(): void
|
||||
* 5) 사용자 퍼미션 오버라이드 (개인 단위 허용/차단)
|
||||
* - 개인 DENY가 최우선 → 해석 레이어에서 우선순위 처리
|
||||
*/
|
||||
if (!Schema::hasTable('user_permission_overrides')) {
|
||||
if (! Schema::hasTable('user_permission_overrides')) {
|
||||
Schema::create('user_permission_overrides', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -206,7 +206,7 @@ private function indexExists(string $table, string $indexName): bool
|
||||
$connection = Schema::getConnection();
|
||||
$schemaManager = $connection->getDoctrineSchemaManager();
|
||||
$doctrineTable = $schemaManager->introspectTable(
|
||||
$connection->getTablePrefix() . $table
|
||||
$connection->getTablePrefix().$table
|
||||
);
|
||||
foreach ($doctrineTable->getIndexes() as $idx) {
|
||||
if ($idx->getName() === $indexName) {
|
||||
@@ -216,6 +216,7 @@ private function indexExists(string $table, string $indexName): bool
|
||||
} catch (\Throwable $e) {
|
||||
// 무시
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
@@ -12,10 +12,10 @@ public function up(): void
|
||||
// ===== permissions =====
|
||||
if (Schema::hasTable('permissions')) {
|
||||
Schema::table('permissions', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('permissions', 'guard_name')) {
|
||||
if (! Schema::hasColumn('permissions', 'guard_name')) {
|
||||
$table->string('guard_name', 50)->default('api')->after('name');
|
||||
}
|
||||
if (!Schema::hasColumn('permissions', 'tenant_id')) {
|
||||
if (! Schema::hasColumn('permissions', 'tenant_id')) {
|
||||
$table->unsignedBigInteger('tenant_id')->nullable()->after('guard_name');
|
||||
$table->index(['tenant_id']);
|
||||
}
|
||||
@@ -35,10 +35,10 @@ public function up(): void
|
||||
// ===== roles =====
|
||||
if (Schema::hasTable('roles')) {
|
||||
Schema::table('roles', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('roles', 'guard_name')) {
|
||||
if (! Schema::hasColumn('roles', 'guard_name')) {
|
||||
$table->string('guard_name', 50)->default('api')->after('name');
|
||||
}
|
||||
if (!Schema::hasColumn('roles', 'tenant_id')) {
|
||||
if (! Schema::hasColumn('roles', 'tenant_id')) {
|
||||
// 이미 있으시다 했지만 혹시 없을 경우 대비
|
||||
$table->unsignedBigInteger('tenant_id')->nullable()->after('guard_name');
|
||||
}
|
||||
@@ -61,7 +61,7 @@ public function up(): void
|
||||
// ===== model_has_roles =====
|
||||
if (Schema::hasTable('model_has_roles')) {
|
||||
Schema::table('model_has_roles', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('model_has_roles', 'tenant_id')) {
|
||||
if (! Schema::hasColumn('model_has_roles', 'tenant_id')) {
|
||||
$table->unsignedBigInteger('tenant_id')->nullable()->after('model_id');
|
||||
}
|
||||
});
|
||||
@@ -88,7 +88,7 @@ public function up(): void
|
||||
// ===== model_has_permissions =====
|
||||
if (Schema::hasTable('model_has_permissions')) {
|
||||
Schema::table('model_has_permissions', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('model_has_permissions', 'tenant_id')) {
|
||||
if (! Schema::hasColumn('model_has_permissions', 'tenant_id')) {
|
||||
$table->unsignedBigInteger('tenant_id')->nullable()->after('model_id');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// 1) 유니크 인덱스 존재 시 제거 (information_schema로 확인)
|
||||
@@ -32,7 +33,7 @@ public function up(): void
|
||||
public function down(): void
|
||||
{
|
||||
// 1) slug 컬럼 복구
|
||||
if (!Schema::hasColumn('menus', 'slug')) {
|
||||
if (! Schema::hasColumn('menus', 'slug')) {
|
||||
Schema::table('menus', function (Blueprint $table) {
|
||||
$table->string('slug', 150)->nullable()->comment('메뉴 슬러그(권한 키)');
|
||||
});
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// 1) parent_id 추가 (tenant_id 뒤에 배치)
|
||||
if (!Schema::hasColumn('departments', 'parent_id')) {
|
||||
if (! Schema::hasColumn('departments', 'parent_id')) {
|
||||
Schema::table('departments', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('parent_id')
|
||||
->nullable()
|
||||
@@ -46,8 +46,8 @@ public function up(): void
|
||||
$sql[] = "MODIFY COLUMN `deleted_by` BIGINT UNSIGNED NULL COMMENT '삭제자 사용자 ID' AFTER `{$after}`";
|
||||
}
|
||||
|
||||
if (!empty($sql)) {
|
||||
DB::statement("ALTER TABLE `departments` " . implode(", ", $sql));
|
||||
if (! empty($sql)) {
|
||||
DB::statement('ALTER TABLE `departments` '.implode(', ', $sql));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,8 +82,8 @@ public function down(): void
|
||||
$sql[] = "MODIFY COLUMN `deleted_by` BIGINT UNSIGNED NULL COMMENT '삭제자 사용자 ID' AFTER `updated_by`";
|
||||
}
|
||||
|
||||
if (!empty($sql)) {
|
||||
DB::statement("ALTER TABLE `departments` " . implode(", ", $sql));
|
||||
if (! empty($sql)) {
|
||||
DB::statement('ALTER TABLE `departments` '.implode(', ', $sql));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
@@ -16,7 +16,7 @@
|
||||
public function up(): void
|
||||
{
|
||||
// 1) permission_overrides (DENY 최우선, 선택적 ALLOW 지원)
|
||||
if (!Schema::hasTable('permission_overrides')) {
|
||||
if (! Schema::hasTable('permission_overrides')) {
|
||||
Schema::create('permission_overrides', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -41,11 +41,11 @@ public function up(): void
|
||||
|
||||
// 2) 혼재 테이블 드롭(존재하면)
|
||||
foreach ([
|
||||
'user_menu_permissions',
|
||||
'role_menu_permissions',
|
||||
'department_permissions',
|
||||
'user_permission_overrides',
|
||||
] as $legacyTable) {
|
||||
'user_menu_permissions',
|
||||
'role_menu_permissions',
|
||||
'department_permissions',
|
||||
'user_permission_overrides',
|
||||
] as $legacyTable) {
|
||||
if (Schema::hasTable($legacyTable)) {
|
||||
Schema::drop($legacyTable);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public function up(): void
|
||||
|
||||
// 3) 보조 인덱스(선택) : model_has_permissions (tenant_id, permission_id)
|
||||
if (Schema::hasTable('model_has_permissions')) {
|
||||
if (!$this->mysqlIndexExists('model_has_permissions', 'idx_mhp_tenant_perm')) {
|
||||
if (! $this->mysqlIndexExists('model_has_permissions', 'idx_mhp_tenant_perm')) {
|
||||
Schema::table('model_has_permissions', function (Blueprint $table) {
|
||||
$table->index(['tenant_id', 'permission_id'], 'idx_mhp_tenant_perm');
|
||||
});
|
||||
@@ -69,7 +69,7 @@ public function down(): void
|
||||
}
|
||||
|
||||
// 2) 드롭했던 테이블 복구(원본 스키마로)
|
||||
if (!Schema::hasTable('user_menu_permissions')) {
|
||||
if (! Schema::hasTable('user_menu_permissions')) {
|
||||
Schema::create('user_menu_permissions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK: 사용자-메뉴 권한 ID');
|
||||
$table->unsignedBigInteger('user_id')->comment('FK: 사용자 ID');
|
||||
@@ -87,7 +87,7 @@ public function down(): void
|
||||
});
|
||||
}
|
||||
|
||||
if (!Schema::hasTable('role_menu_permissions')) {
|
||||
if (! Schema::hasTable('role_menu_permissions')) {
|
||||
Schema::create('role_menu_permissions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK: 역할-메뉴 권한 ID');
|
||||
$table->unsignedBigInteger('role_id')->comment('FK: 역할 ID');
|
||||
@@ -105,7 +105,7 @@ public function down(): void
|
||||
});
|
||||
}
|
||||
|
||||
if (!Schema::hasTable('department_permissions')) {
|
||||
if (! Schema::hasTable('department_permissions')) {
|
||||
Schema::create('department_permissions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -123,7 +123,7 @@ public function down(): void
|
||||
});
|
||||
}
|
||||
|
||||
if (!Schema::hasTable('user_permission_overrides')) {
|
||||
if (! Schema::hasTable('user_permission_overrides')) {
|
||||
Schema::create('user_permission_overrides', function (Blueprint $table) {
|
||||
$table->bigIncrements('id')->comment('PK');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
@@ -11,7 +11,7 @@ public function up(): void
|
||||
{
|
||||
Schema::table('materials', function (Blueprint $table) {
|
||||
// category_id
|
||||
if (!Schema::hasColumn('materials', 'category_id')) {
|
||||
if (! Schema::hasColumn('materials', 'category_id')) {
|
||||
$table->unsignedBigInteger('category_id')
|
||||
->nullable()
|
||||
->after('tenant_id')
|
||||
@@ -20,7 +20,7 @@ public function up(): void
|
||||
}
|
||||
|
||||
// item_name
|
||||
if (!Schema::hasColumn('materials', 'item_name')) {
|
||||
if (! Schema::hasColumn('materials', 'item_name')) {
|
||||
$table->string('item_name', 255)
|
||||
->nullable()
|
||||
->after('name')
|
||||
@@ -40,10 +40,19 @@ public function up(): void
|
||||
public function down(): void
|
||||
{
|
||||
// FK 제거 시도
|
||||
try { DB::statement('ALTER TABLE materials DROP FOREIGN KEY materials_category_id_foreign'); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement('ALTER TABLE materials DROP FOREIGN KEY materials_category_id_foreign');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
// 컬럼 제거
|
||||
try { DB::statement('ALTER TABLE materials DROP COLUMN category_id'); } catch (\Throwable $e) {}
|
||||
try { DB::statement('ALTER TABLE materials DROP COLUMN item_name'); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement('ALTER TABLE materials DROP COLUMN category_id');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
try {
|
||||
DB::statement('ALTER TABLE materials DROP COLUMN item_name');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,8 +12,10 @@ private function dropUniqueIfColumns(string $table, array $columns): void
|
||||
{
|
||||
$indexes = collect(DB::select("SHOW INDEX FROM `{$table}`"))->groupBy('Key_name');
|
||||
foreach ($indexes as $name => $rows) {
|
||||
$nonUnique = (int)($rows->first()->Non_unique ?? 1);
|
||||
if ($nonUnique !== 0) continue;
|
||||
$nonUnique = (int) ($rows->first()->Non_unique ?? 1);
|
||||
if ($nonUnique !== 0) {
|
||||
continue;
|
||||
}
|
||||
$cols = collect($rows)->sortBy('Seq_in_index')->pluck('Column_name')->values()->all();
|
||||
if ($cols === $columns) {
|
||||
DB::statement("ALTER TABLE `{$table}` DROP INDEX `{$name}`");
|
||||
@@ -48,8 +50,14 @@ public function up(): void
|
||||
* - profile_code 컬럼 추가(+ 인덱스)
|
||||
*/
|
||||
// A-1. 값 변환(Y/N → 1/0) 시도 (이미 숫자여도 무해)
|
||||
try { DB::statement("UPDATE categories SET is_active = 1 WHERE is_active = 'Y'"); } catch (\Throwable $e) {}
|
||||
try { DB::statement("UPDATE categories SET is_active = 0 WHERE is_active = 'N'"); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement("UPDATE categories SET is_active = 1 WHERE is_active = 'Y'");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
try {
|
||||
DB::statement("UPDATE categories SET is_active = 0 WHERE is_active = 'N'");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
// A-2. 타입 변경
|
||||
DB::statement("ALTER TABLE categories
|
||||
@@ -57,21 +65,24 @@ public function up(): void
|
||||
|
||||
// A-3. 유니크 (tenant_id, code) → (tenant_id, code_group, code)
|
||||
$this->dropUniqueIfColumns('categories', ['tenant_id', 'code']);
|
||||
$idx = collect(DB::select("SHOW INDEX FROM `categories`"))->groupBy('Key_name');
|
||||
$idx = collect(DB::select('SHOW INDEX FROM `categories`'))->groupBy('Key_name');
|
||||
$hasTarget = false;
|
||||
foreach ($idx as $name => $rows) {
|
||||
$nonUnique = (int)($rows->first()->Non_unique ?? 1);
|
||||
$nonUnique = (int) ($rows->first()->Non_unique ?? 1);
|
||||
$cols = collect($rows)->sortBy('Seq_in_index')->pluck('Column_name')->values()->all();
|
||||
if ($nonUnique === 0 && $cols === ['tenant_id', 'code_group', 'code']) { $hasTarget = true; break; }
|
||||
if ($nonUnique === 0 && $cols === ['tenant_id', 'code_group', 'code']) {
|
||||
$hasTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$hasTarget) {
|
||||
DB::statement("ALTER TABLE `categories`
|
||||
ADD UNIQUE KEY `uq_tenant_codegroup_code` (`tenant_id`,`code_group`,`code`)");
|
||||
if (! $hasTarget) {
|
||||
DB::statement('ALTER TABLE `categories`
|
||||
ADD UNIQUE KEY `uq_tenant_codegroup_code` (`tenant_id`,`code_group`,`code`)');
|
||||
}
|
||||
|
||||
// A-4. profile_code 추가 (common_codes.code, code_group='capability_profile')
|
||||
Schema::table('categories', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('categories', 'profile_code')) {
|
||||
if (! Schema::hasColumn('categories', 'profile_code')) {
|
||||
$table->string('profile_code', 30)
|
||||
->nullable()
|
||||
->after('code_group')
|
||||
@@ -79,10 +90,10 @@ public function up(): void
|
||||
}
|
||||
});
|
||||
// 인덱스 (tenant_id, profile_code)
|
||||
$hasIdx = collect(DB::select("SHOW INDEX FROM `categories`"))
|
||||
->contains(fn($r) => $r->Key_name === 'idx_categories_tenant_profilecode');
|
||||
if (!$hasIdx) {
|
||||
DB::statement("CREATE INDEX `idx_categories_tenant_profilecode` ON `categories` (`tenant_id`, `profile_code`)");
|
||||
$hasIdx = collect(DB::select('SHOW INDEX FROM `categories`'))
|
||||
->contains(fn ($r) => $r->Key_name === 'idx_categories_tenant_profilecode');
|
||||
if (! $hasIdx) {
|
||||
DB::statement('CREATE INDEX `idx_categories_tenant_profilecode` ON `categories` (`tenant_id`, `profile_code`)');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,19 +107,19 @@ public function up(): void
|
||||
->references('id')->on('categories')
|
||||
->onUpdate('cascade')->onDelete('restrict');
|
||||
|
||||
if (!Schema::hasColumn('products', 'product_type')) {
|
||||
if (! Schema::hasColumn('products', 'product_type')) {
|
||||
$table->string('product_type', 30)
|
||||
->default('PRODUCT')
|
||||
->comment("제품유형: PRODUCT/PART/SUBASSEMBLY 등 (common_codes.code_group='product_type')")
|
||||
->after('category_id');
|
||||
}
|
||||
if (!Schema::hasColumn('products', 'is_sellable')) {
|
||||
if (! Schema::hasColumn('products', 'is_sellable')) {
|
||||
$table->tinyInteger('is_sellable')->default(1)->comment('판매가능(1/0)')->after('description');
|
||||
}
|
||||
if (!Schema::hasColumn('products', 'is_purchasable')) {
|
||||
if (! Schema::hasColumn('products', 'is_purchasable')) {
|
||||
$table->tinyInteger('is_purchasable')->default(0)->comment('구매가능(1/0)')->after('is_sellable');
|
||||
}
|
||||
if (!Schema::hasColumn('products', 'is_producible')) {
|
||||
if (! Schema::hasColumn('products', 'is_producible')) {
|
||||
$table->tinyInteger('is_producible')->default(1)->comment('제조가능(1/0)')->after('is_purchasable');
|
||||
}
|
||||
});
|
||||
@@ -117,7 +128,7 @@ public function up(): void
|
||||
* C) BOM 자기참조 테이블 신설 (product_components)
|
||||
* - parts/boms/bom_items 레거시 제거
|
||||
*/
|
||||
if (!Schema::hasTable('product_components')) {
|
||||
if (! Schema::hasTable('product_components')) {
|
||||
Schema::create('product_components', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
@@ -155,7 +166,7 @@ public function down(): void
|
||||
if (Schema::hasTable('product_components')) {
|
||||
Schema::drop('product_components');
|
||||
}
|
||||
if (!Schema::hasTable('parts')) {
|
||||
if (! Schema::hasTable('parts')) {
|
||||
Schema::create('parts', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id');
|
||||
@@ -171,10 +182,10 @@ public function down(): void
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
$table->unique(['tenant_id','code']);
|
||||
$table->unique(['tenant_id', 'code']);
|
||||
});
|
||||
}
|
||||
if (!Schema::hasTable('boms')) {
|
||||
if (! Schema::hasTable('boms')) {
|
||||
Schema::create('boms', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id');
|
||||
@@ -191,10 +202,10 @@ public function down(): void
|
||||
$table->unsignedBigInteger('updated_by')->nullable();
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
$table->unique(['tenant_id','product_id','code']);
|
||||
$table->unique(['tenant_id', 'product_id', 'code']);
|
||||
});
|
||||
}
|
||||
if (!Schema::hasTable('bom_items')) {
|
||||
if (! Schema::hasTable('bom_items')) {
|
||||
Schema::create('bom_items', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id');
|
||||
@@ -218,10 +229,18 @@ public function down(): void
|
||||
* - FK: categories → common_codes
|
||||
*/
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('products', 'is_sellable')) $table->dropColumn('is_sellable');
|
||||
if (Schema::hasColumn('products', 'is_purchasable')) $table->dropColumn('is_purchasable');
|
||||
if (Schema::hasColumn('products', 'is_producible')) $table->dropColumn('is_producible');
|
||||
if (Schema::hasColumn('products', 'product_type')) $table->dropColumn('product_type');
|
||||
if (Schema::hasColumn('products', 'is_sellable')) {
|
||||
$table->dropColumn('is_sellable');
|
||||
}
|
||||
if (Schema::hasColumn('products', 'is_purchasable')) {
|
||||
$table->dropColumn('is_purchasable');
|
||||
}
|
||||
if (Schema::hasColumn('products', 'is_producible')) {
|
||||
$table->dropColumn('is_producible');
|
||||
}
|
||||
if (Schema::hasColumn('products', 'product_type')) {
|
||||
$table->dropColumn('product_type');
|
||||
}
|
||||
});
|
||||
|
||||
$this->dropProductsCategoryFk();
|
||||
@@ -237,10 +256,10 @@ public function down(): void
|
||||
* - 유니크: (tenant_id, code_group, code) → (tenant_id, code)
|
||||
* - is_active: CHAR('Y'/'N') 복구 + 값 재변환
|
||||
*/
|
||||
$hasIdx = collect(DB::select("SHOW INDEX FROM `categories`"))
|
||||
->contains(fn($r) => $r->Key_name === 'idx_categories_tenant_profilecode');
|
||||
$hasIdx = collect(DB::select('SHOW INDEX FROM `categories`'))
|
||||
->contains(fn ($r) => $r->Key_name === 'idx_categories_tenant_profilecode');
|
||||
if ($hasIdx) {
|
||||
DB::statement("DROP INDEX `idx_categories_tenant_profilecode` ON `categories`");
|
||||
DB::statement('DROP INDEX `idx_categories_tenant_profilecode` ON `categories`');
|
||||
}
|
||||
Schema::table('categories', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('categories', 'profile_code')) {
|
||||
@@ -248,27 +267,36 @@ public function down(): void
|
||||
}
|
||||
});
|
||||
|
||||
$idx = collect(DB::select("SHOW INDEX FROM `categories`"))->groupBy('Key_name');
|
||||
$idx = collect(DB::select('SHOW INDEX FROM `categories`'))->groupBy('Key_name');
|
||||
if ($idx->has('uq_tenant_codegroup_code')) {
|
||||
DB::statement("ALTER TABLE `categories` DROP INDEX `uq_tenant_codegroup_code`");
|
||||
DB::statement('ALTER TABLE `categories` DROP INDEX `uq_tenant_codegroup_code`');
|
||||
}
|
||||
// (tenant_id, code) 유니크 복구(없을 때만)
|
||||
$idx = collect(DB::select("SHOW INDEX FROM `categories`"))->groupBy('Key_name');
|
||||
$idx = collect(DB::select('SHOW INDEX FROM `categories`'))->groupBy('Key_name');
|
||||
$hasOld = false;
|
||||
foreach ($idx as $name => $rows) {
|
||||
$nonUnique = (int)($rows->first()->Non_unique ?? 1);
|
||||
$nonUnique = (int) ($rows->first()->Non_unique ?? 1);
|
||||
$cols = collect($rows)->sortBy('Seq_in_index')->pluck('Column_name')->values()->all();
|
||||
if ($nonUnique === 0 && $cols === ['tenant_id', 'code']) { $hasOld = true; break; }
|
||||
if ($nonUnique === 0 && $cols === ['tenant_id', 'code']) {
|
||||
$hasOld = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$hasOld) {
|
||||
DB::statement("ALTER TABLE `categories`
|
||||
ADD UNIQUE KEY `categories_tenant_id_code_unique` (`tenant_id`,`code`)");
|
||||
if (! $hasOld) {
|
||||
DB::statement('ALTER TABLE `categories`
|
||||
ADD UNIQUE KEY `categories_tenant_id_code_unique` (`tenant_id`,`code`)');
|
||||
}
|
||||
|
||||
// 타입 복구 + 값 재변환
|
||||
DB::statement("ALTER TABLE categories
|
||||
MODIFY COLUMN is_active CHAR(1) NOT NULL DEFAULT 'Y' COMMENT '활성여부(Y/N)'");
|
||||
try { DB::statement("UPDATE categories SET is_active = 'Y' WHERE is_active = 1"); } catch (\Throwable $e) {}
|
||||
try { DB::statement("UPDATE categories SET is_active = 'N' WHERE is_active = 0"); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement("UPDATE categories SET is_active = 'Y' WHERE is_active = 1");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
try {
|
||||
DB::statement("UPDATE categories SET is_active = 'N' WHERE is_active = 0");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tenant_bootstrap_runs', function (Blueprint $t) {
|
||||
$t->bigIncrements('id');
|
||||
$t->unsignedBigInteger('tenant_id');
|
||||
@@ -19,7 +21,9 @@ public function up(): void {
|
||||
$t->index(['tenant_id', 'recipe']);
|
||||
});
|
||||
}
|
||||
public function down(): void {
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tenant_bootstrap_runs');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
|
||||
@@ -37,12 +37,12 @@ public function up(): void
|
||||
// 3) 기존 유니크 키 재정의:
|
||||
// (tenant_id, parent_product_id, ref_type, child_product_id, material_id, sort_order)
|
||||
// - 제품/자재 타입과 각각의 ID를 모두 포함하도록 변경
|
||||
DB::statement("ALTER TABLE product_components DROP INDEX uq_component_row");
|
||||
DB::statement("
|
||||
DB::statement('ALTER TABLE product_components DROP INDEX uq_component_row');
|
||||
DB::statement('
|
||||
ALTER TABLE product_components
|
||||
ADD UNIQUE INDEX uq_component_row
|
||||
(tenant_id, parent_product_id, ref_type, child_product_id, material_id, sort_order)
|
||||
");
|
||||
');
|
||||
|
||||
// 4) (선택) CHECK 제약: MySQL 8.0.16+ 에서만 유효
|
||||
// ref_type=PRODUCT -> child_product_id NOT NULL AND material_id NULL
|
||||
@@ -69,25 +69,25 @@ public function down(): void
|
||||
{
|
||||
// CHECK 제약 삭제 (가능한 경우만)
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE product_components
|
||||
DROP CHECK chk_ref_type_consistency
|
||||
");
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
// 무시
|
||||
}
|
||||
|
||||
// 유니크 키를 원래대로 복구
|
||||
try {
|
||||
DB::statement("ALTER TABLE product_components DROP INDEX uq_component_row");
|
||||
DB::statement('ALTER TABLE product_components DROP INDEX uq_component_row');
|
||||
} catch (\Throwable $e) {
|
||||
// 무시
|
||||
}
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE product_components
|
||||
ADD UNIQUE INDEX uq_component_row
|
||||
(tenant_id, parent_product_id, child_product_id, sort_order)
|
||||
");
|
||||
');
|
||||
|
||||
Schema::table('product_components', function (Blueprint $table) {
|
||||
// FK 우선 제거
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// 1) 기존 제약/인덱스 해제 (있으면 제거)
|
||||
@@ -15,7 +16,10 @@ public function up(): void
|
||||
'product_components_tenant_id_parent_product_id_index',
|
||||
];
|
||||
foreach ($dropIndexes as $idx) {
|
||||
try { DB::statement("ALTER TABLE `product_components` DROP INDEX `$idx`"); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement("ALTER TABLE `product_components` DROP INDEX `$idx`");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
|
||||
$dropFks = [
|
||||
@@ -24,26 +28,32 @@ public function up(): void
|
||||
'product_components_parent_product_id_foreign',
|
||||
];
|
||||
foreach ($dropFks as $fk) {
|
||||
try { DB::statement("ALTER TABLE `product_components` DROP FOREIGN KEY `$fk`"); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement("ALTER TABLE `product_components` DROP FOREIGN KEY `$fk`");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 데이터 손실 허용 → TRUNCATE 로 비우고 진행
|
||||
try { DB::statement("TRUNCATE TABLE `product_components`"); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement('TRUNCATE TABLE `product_components`');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
// 3) 컬럼 추가/수정
|
||||
Schema::table('product_components', function (Blueprint $table) {
|
||||
// 프론트 카테고리 메타(선택 저장)
|
||||
if (!Schema::hasColumn('product_components', 'category_id')) {
|
||||
if (! Schema::hasColumn('product_components', 'category_id')) {
|
||||
$table->unsignedBigInteger('category_id')->nullable()->after('parent_product_id')
|
||||
->comment('프론트 카테고리 ID(선택)');
|
||||
}
|
||||
if (!Schema::hasColumn('product_components', 'category_name')) {
|
||||
if (! Schema::hasColumn('product_components', 'category_name')) {
|
||||
$table->string('category_name', 100)->nullable()->after('category_id')
|
||||
->comment('프론트 카테고리명(선택)');
|
||||
}
|
||||
|
||||
// 통합 참조키 ref_id 추가
|
||||
if (!Schema::hasColumn('product_components', 'ref_id')) {
|
||||
if (! Schema::hasColumn('product_components', 'ref_id')) {
|
||||
$table->unsignedBigInteger('ref_id')->nullable()->after('ref_type')
|
||||
->comment('참조 ID (materials.id 또는 products.id)');
|
||||
}
|
||||
@@ -95,23 +105,29 @@ public function up(): void
|
||||
public function down(): void
|
||||
{
|
||||
// 인덱스 제거
|
||||
foreach (['idx_tenant_parent','idx_tenant_ref','idx_tenant_category','idx_tenant_sort'] as $idx) {
|
||||
try { DB::statement("ALTER TABLE `product_components` DROP INDEX `$idx`"); } catch (\Throwable $e) {}
|
||||
foreach (['idx_tenant_parent', 'idx_tenant_ref', 'idx_tenant_category', 'idx_tenant_sort'] as $idx) {
|
||||
try {
|
||||
DB::statement("ALTER TABLE `product_components` DROP INDEX `$idx`");
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
|
||||
// 데이터 손실 허용: TRUNCATE 후 원형에 가깝게 복원
|
||||
try { DB::statement("TRUNCATE TABLE `product_components`"); } catch (\Throwable $e) {}
|
||||
try {
|
||||
DB::statement('TRUNCATE TABLE `product_components`');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
// 컬럼 복원
|
||||
Schema::table('product_components', function (Blueprint $table) {
|
||||
// child_product_id, material_id, is_default 복원
|
||||
if (!Schema::hasColumn('product_components', 'child_product_id')) {
|
||||
if (! Schema::hasColumn('product_components', 'child_product_id')) {
|
||||
$table->unsignedBigInteger('child_product_id')->nullable()->after('ref_type')->comment('하위 제품/부품 ID');
|
||||
}
|
||||
if (!Schema::hasColumn('product_components', 'material_id')) {
|
||||
if (! Schema::hasColumn('product_components', 'material_id')) {
|
||||
$table->unsignedBigInteger('material_id')->nullable()->after('child_product_id')->comment('자재 ID');
|
||||
}
|
||||
if (!Schema::hasColumn('product_components', 'is_default')) {
|
||||
if (! Schema::hasColumn('product_components', 'is_default')) {
|
||||
$table->tinyInteger('is_default')->default(0)->after('sort_order')->comment('기본 BOM 여부(1/0)');
|
||||
}
|
||||
});
|
||||
@@ -124,10 +140,10 @@ public function down(): void
|
||||
");
|
||||
|
||||
// quantity 정밀도 원복
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE `product_components`
|
||||
MODIFY COLUMN `quantity` DECIMAL(18,4) NOT NULL DEFAULT 1.0000
|
||||
");
|
||||
');
|
||||
|
||||
// ref_id 제거
|
||||
if (Schema::hasColumn('product_components', 'ref_id')) {
|
||||
@@ -148,49 +164,55 @@ public function down(): void
|
||||
|
||||
// 원래 인덱스/제약 복원 (FK 최소화 정책이지만 down에서는 원형 회귀)
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE `product_components`
|
||||
ADD CONSTRAINT `uq_component_row`
|
||||
UNIQUE (`tenant_id`,`parent_product_id`,`ref_type`,`child_product_id`,`material_id`,`sort_order`)
|
||||
");
|
||||
} catch (\Throwable $e) {}
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE `product_components`
|
||||
ADD CONSTRAINT `product_components_child_product_id_foreign`
|
||||
FOREIGN KEY (`child_product_id`) REFERENCES `products`(`id`)
|
||||
");
|
||||
} catch (\Throwable $e) {}
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE `product_components`
|
||||
ADD CONSTRAINT `product_components_material_id_foreign`
|
||||
FOREIGN KEY (`material_id`) REFERENCES `materials`(`id`) ON DELETE SET NULL
|
||||
");
|
||||
} catch (\Throwable $e) {}
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
ALTER TABLE `product_components`
|
||||
ADD CONSTRAINT `product_components_parent_product_id_foreign`
|
||||
FOREIGN KEY (`parent_product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE
|
||||
");
|
||||
} catch (\Throwable $e) {}
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
CREATE INDEX `product_components_tenant_id_child_product_id_index`
|
||||
ON `product_components`(`tenant_id`,`child_product_id`)
|
||||
");
|
||||
} catch (\Throwable $e) {}
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
DB::statement("
|
||||
DB::statement('
|
||||
CREATE INDEX `product_components_tenant_id_parent_product_id_index`
|
||||
ON `product_components`(`tenant_id`,`parent_product_id`)
|
||||
");
|
||||
} catch (\Throwable $e) {}
|
||||
');
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
// 설계 상위: models
|
||||
public function up(): void {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('models', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트ID');
|
||||
@@ -25,7 +27,8 @@ public function up(): void {
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('models');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
// 설계 버전: model_versions
|
||||
public function up(): void {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('model_versions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트ID');
|
||||
@@ -26,7 +28,8 @@ public function up(): void {
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('model_versions');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
// 설계용 BOM 템플릿: bom_templates
|
||||
public function up(): void {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('bom_templates', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트ID');
|
||||
@@ -23,7 +25,8 @@ public function up(): void {
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('bom_templates');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
// 설계용 BOM 항목: bom_template_items
|
||||
public function up(): void {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('bom_template_items', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트ID');
|
||||
@@ -26,7 +28,8 @@ public function up(): void {
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('bom_template_items');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('audit_logs', function (Blueprint $table) {
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* BOM 테이블에 산출식 관련 필드 추가
|
||||
* - bom_templates: 견적시 필요한 파라미터 스키마 정의
|
||||
* - bom_template_items: 개별 아이템의 계산식 정의
|
||||
*/
|
||||
public function up(): void {
|
||||
public function up(): void
|
||||
{
|
||||
// BOM 템플릿에 산출식 조건 스키마 추가
|
||||
Schema::table('bom_templates', function (Blueprint $table) {
|
||||
$table->json('calculation_schema')->nullable()->comment('견적 파라미터 스키마 (JSON)');
|
||||
@@ -30,7 +32,8 @@ public function up(): void {
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void {
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('bom_template_items', function (Blueprint $table) {
|
||||
$table->dropColumn(['is_calculated', 'calculation_formula', 'depends_on', 'calculation_config']);
|
||||
});
|
||||
@@ -39,4 +42,4 @@ public function down(): void {
|
||||
$table->dropColumn(['calculation_schema', 'company_type', 'formula_version']);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 업체별 산출식 설정 테이블 생성
|
||||
* 다양한 업체(경동기업, 삼성물산 등)의 고유한 산출식을 관리
|
||||
*/
|
||||
public function up(): void {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('calculation_configs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트ID');
|
||||
@@ -35,7 +37,8 @@ public function up(): void {
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void {
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('calculation_configs');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
@@ -60,152 +58,152 @@ public function up(): void
|
||||
$screenFields = [
|
||||
// 기본 정보
|
||||
['key' => 'model_name', 'name' => '모델명', 'type' => 'select', 'required' => true, 'order' => 1,
|
||||
'options' => ['KSS01', 'KSS02', 'KSE01', 'KWE01', 'KDSS01', '스크린비인정'],
|
||||
'desc' => '스크린 제품 모델 선택 (col4)'],
|
||||
'options' => ['KSS01', 'KSS02', 'KSE01', 'KWE01', 'KDSS01', '스크린비인정'],
|
||||
'desc' => '스크린 제품 모델 선택 (col4)'],
|
||||
|
||||
['key' => 'sequence', 'name' => '순번', 'type' => 'text', 'required' => false, 'order' => 2,
|
||||
'desc' => '견적 항목 순번 (col1)'],
|
||||
'desc' => '견적 항목 순번 (col1)'],
|
||||
|
||||
['key' => 'product_category', 'name' => '대분류', 'type' => 'text', 'required' => true, 'order' => 3,
|
||||
'default' => '스크린', 'desc' => '제품 대분류 (col2)'],
|
||||
'default' => '스크린', 'desc' => '제품 대분류 (col2)'],
|
||||
|
||||
['key' => 'sub_category', 'name' => '중분류', 'type' => 'text', 'required' => false, 'order' => 4,
|
||||
'desc' => '제품 중분류 (col3)'],
|
||||
'desc' => '제품 중분류 (col3)'],
|
||||
|
||||
// 사이즈 관련
|
||||
['key' => 'open_width', 'name' => '오픈사이즈 가로(mm)', 'type' => 'number', 'required' => true, 'order' => 10,
|
||||
'desc' => '개구부 가로 사이즈 W0'],
|
||||
'desc' => '개구부 가로 사이즈 W0'],
|
||||
|
||||
['key' => 'open_height', 'name' => '오픈사이즈 세로(mm)', 'type' => 'number', 'required' => true, 'order' => 11,
|
||||
'desc' => '개구부 세로 사이즈 H0'],
|
||||
'desc' => '개구부 세로 사이즈 H0'],
|
||||
|
||||
['key' => 'make_width', 'name' => '제작사이즈 가로(mm)', 'type' => 'number', 'required' => false, 'order' => 12,
|
||||
'desc' => '제작 가로 사이즈 W1 (자동계산, col10)'],
|
||||
'desc' => '제작 가로 사이즈 W1 (자동계산, col10)'],
|
||||
|
||||
['key' => 'make_height', 'name' => '제작사이즈 세로(mm)', 'type' => 'number', 'required' => false, 'order' => 13,
|
||||
'desc' => '제작 세로 사이즈 H1 (자동계산, col11)'],
|
||||
'desc' => '제작 세로 사이즈 H1 (자동계산, col11)'],
|
||||
|
||||
['key' => 'quantity', 'name' => '수량', 'type' => 'number', 'required' => true, 'order' => 14,
|
||||
'default' => '1', 'desc' => '제품 수량 (col14)'],
|
||||
'default' => '1', 'desc' => '제품 수량 (col14)'],
|
||||
|
||||
// 부품 관련
|
||||
['key' => 'guide_rail_type', 'name' => '가이드레일 유형', 'type' => 'select', 'required' => true, 'order' => 20,
|
||||
'options' => ['벽면형', '측면형', '혼합형'],
|
||||
'desc' => '가이드레일 설치 방식 (col6)'],
|
||||
'options' => ['벽면형', '측면형', '혼합형'],
|
||||
'desc' => '가이드레일 설치 방식 (col6)'],
|
||||
|
||||
['key' => 'shutter_box', 'name' => '셔터박스', 'type' => 'select', 'required' => false, 'order' => 30,
|
||||
'options' => ['', '500*380', '500*350', 'custom'],
|
||||
'desc' => '셔터박스 사이즈 선택 (col36)'],
|
||||
'options' => ['', '500*380', '500*350', 'custom'],
|
||||
'desc' => '셔터박스 사이즈 선택 (col36)'],
|
||||
|
||||
['key' => 'shutter_box_custom', 'name' => '셔터박스 직접입력', 'type' => 'text', 'required' => false, 'order' => 31,
|
||||
'desc' => '셔터박스 직접입력 시 사이즈'],
|
||||
'desc' => '셔터박스 직접입력 시 사이즈'],
|
||||
|
||||
['key' => 'front_bottom', 'name' => '전면밑', 'type' => 'number', 'required' => false, 'order' => 32,
|
||||
'default' => '50', 'desc' => '전면밑 치수 (mm)'],
|
||||
'default' => '50', 'desc' => '전면밑 치수 (mm)'],
|
||||
|
||||
['key' => 'rail_width', 'name' => '레일폭', 'type' => 'number', 'required' => false, 'order' => 33,
|
||||
'default' => '70', 'desc' => '레일 폭 치수 (mm)'],
|
||||
'default' => '70', 'desc' => '레일 폭 치수 (mm)'],
|
||||
|
||||
['key' => 'box_direction', 'name' => '박스방향', 'type' => 'select', 'required' => false, 'order' => 34,
|
||||
'options' => ['양면', '밑면', '후면'],
|
||||
'default' => '양면', 'desc' => '셔터박스 설치 방향'],
|
||||
'options' => ['양면', '밑면', '후면'],
|
||||
'default' => '양면', 'desc' => '셔터박스 설치 방향'],
|
||||
|
||||
// 모터 관련
|
||||
['key' => 'motor_bracket_size', 'name' => '모터브라켓 사이즈', 'type' => 'text', 'required' => false, 'order' => 40,
|
||||
'desc' => '중량+인치 기반 자동계산 브라켓 사이즈'],
|
||||
'desc' => '중량+인치 기반 자동계산 브라켓 사이즈'],
|
||||
|
||||
['key' => 'motor_capacity', 'name' => '모터 용량', 'type' => 'text', 'required' => false, 'order' => 41,
|
||||
'desc' => '계산된 모터 용량'],
|
||||
'desc' => '계산된 모터 용량'],
|
||||
|
||||
['key' => 'shaft_inch', 'name' => '샤프트 인치', 'type' => 'select', 'required' => false, 'order' => 42,
|
||||
'options' => ['4', '5', '6', '8'],
|
||||
'desc' => '샤프트 사이즈 (인치)'],
|
||||
'options' => ['4', '5', '6', '8'],
|
||||
'desc' => '샤프트 사이즈 (인치)'],
|
||||
|
||||
// 마구리 관련
|
||||
['key' => 'maguri_length', 'name' => '마구리 길이', 'type' => 'number', 'required' => false, 'order' => 50,
|
||||
'desc' => '마구리 길이 치수 (col45)'],
|
||||
'desc' => '마구리 길이 치수 (col45)'],
|
||||
|
||||
['key' => 'maguri_wing', 'name' => '마구리 윙', 'type' => 'number', 'required' => false, 'order' => 51,
|
||||
'desc' => '마구리 윙 길이 치수'],
|
||||
'desc' => '마구리 윙 길이 치수'],
|
||||
|
||||
// 계산 결과
|
||||
['key' => 'calculated_weight', 'name' => '계산 중량', 'type' => 'number', 'required' => false, 'order' => 60,
|
||||
'desc' => '자동 계산된 중량 (kg)'],
|
||||
'desc' => '자동 계산된 중량 (kg)'],
|
||||
|
||||
['key' => 'calculated_area', 'name' => '계산 면적', 'type' => 'number', 'required' => false, 'order' => 61,
|
||||
'desc' => '자동 계산된 면적 (㎡)'],
|
||||
'desc' => '자동 계산된 면적 (㎡)'],
|
||||
|
||||
['key' => 'unit_price', 'name' => '단가', 'type' => 'number', 'required' => false, 'order' => 70,
|
||||
'desc' => '제품 단가 (원)'],
|
||||
'desc' => '제품 단가 (원)'],
|
||||
|
||||
['key' => 'total_price', 'name' => '금액', 'type' => 'number', 'required' => false, 'order' => 71,
|
||||
'desc' => '총 금액 (원)'],
|
||||
'desc' => '총 금액 (원)'],
|
||||
];
|
||||
|
||||
// 5. 철재 카테고리의 동적 필드들
|
||||
$steelFields = [
|
||||
// 기본 정보
|
||||
['key' => 'model_name', 'name' => '모델명', 'type' => 'select', 'required' => true, 'order' => 1,
|
||||
'options' => ['KQTS01', 'KTE01', '철재비인정'],
|
||||
'desc' => '철재 제품 모델 선택'],
|
||||
'options' => ['KQTS01', 'KTE01', '철재비인정'],
|
||||
'desc' => '철재 제품 모델 선택'],
|
||||
|
||||
['key' => 'sequence', 'name' => '순번', 'type' => 'text', 'required' => false, 'order' => 2,
|
||||
'desc' => '견적 항목 순번'],
|
||||
'desc' => '견적 항목 순번'],
|
||||
|
||||
['key' => 'product_category', 'name' => '대분류', 'type' => 'text', 'required' => true, 'order' => 3,
|
||||
'default' => '철재', 'desc' => '제품 대분류'],
|
||||
'default' => '철재', 'desc' => '제품 대분류'],
|
||||
|
||||
// 사이즈 관련 (철재는 다른 계산식)
|
||||
['key' => 'open_width', 'name' => '오픈사이즈 가로(mm)', 'type' => 'number', 'required' => true, 'order' => 10,
|
||||
'desc' => '개구부 가로 사이즈 W0'],
|
||||
'desc' => '개구부 가로 사이즈 W0'],
|
||||
|
||||
['key' => 'open_height', 'name' => '오픈사이즈 세로(mm)', 'type' => 'number', 'required' => true, 'order' => 11,
|
||||
'desc' => '개구부 세로 사이즈 H0'],
|
||||
'desc' => '개구부 세로 사이즈 H0'],
|
||||
|
||||
['key' => 'make_width', 'name' => '제작사이즈 가로(mm)', 'type' => 'number', 'required' => false, 'order' => 12,
|
||||
'desc' => '제작 가로 사이즈 W1 (W0+110)'],
|
||||
'desc' => '제작 가로 사이즈 W1 (W0+110)'],
|
||||
|
||||
['key' => 'make_height', 'name' => '제작사이즈 세로(mm)', 'type' => 'number', 'required' => false, 'order' => 13,
|
||||
'desc' => '제작 세로 사이즈 H1 (H0+350)'],
|
||||
'desc' => '제작 세로 사이즈 H1 (H0+350)'],
|
||||
|
||||
['key' => 'quantity', 'name' => '수량', 'type' => 'number', 'required' => true, 'order' => 14,
|
||||
'default' => '1', 'desc' => '제품 수량'],
|
||||
'default' => '1', 'desc' => '제품 수량'],
|
||||
|
||||
// 철재 특화 필드
|
||||
['key' => 'slat_thickness', 'name' => '스라트 두께', 'type' => 'select', 'required' => true, 'order' => 20,
|
||||
'options' => ['0.8mm', '1.0mm', '1.2mm', '1.5mm', '2.0mm'],
|
||||
'desc' => '철재 스라트 두께'],
|
||||
'options' => ['0.8mm', '1.0mm', '1.2mm', '1.5mm', '2.0mm'],
|
||||
'desc' => '철재 스라트 두께'],
|
||||
|
||||
['key' => 'bending_work', 'name' => '절곡 가공', 'type' => 'checkbox', 'required' => false, 'order' => 21,
|
||||
'desc' => '절곡 가공 여부'],
|
||||
'desc' => '절곡 가공 여부'],
|
||||
|
||||
['key' => 'welding_work', 'name' => '용접 가공', 'type' => 'checkbox', 'required' => false, 'order' => 22,
|
||||
'desc' => '용접 가공 여부'],
|
||||
'desc' => '용접 가공 여부'],
|
||||
|
||||
// 환봉, 각파이프 등
|
||||
['key' => 'round_bar_quantity', 'name' => '환봉 수량', 'type' => 'number', 'required' => false, 'order' => 30,
|
||||
'desc' => '환봉 필요 수량 (자동계산)'],
|
||||
'desc' => '환봉 필요 수량 (자동계산)'],
|
||||
|
||||
['key' => 'square_pipe', 'name' => '각파이프', 'type' => 'text', 'required' => false, 'order' => 31,
|
||||
'desc' => '각파이프 사양'],
|
||||
'desc' => '각파이프 사양'],
|
||||
|
||||
// 모터 관련 (철재용)
|
||||
['key' => 'motor_bracket_size', 'name' => '모터브라켓 사이즈', 'type' => 'text', 'required' => false, 'order' => 40,
|
||||
'desc' => '중량+인치 기반 브라켓 사이즈'],
|
||||
'desc' => '중량+인치 기반 브라켓 사이즈'],
|
||||
|
||||
['key' => 'shaft_inch', 'name' => '샤프트 인치', 'type' => 'select', 'required' => false, 'order' => 41,
|
||||
'options' => ['4', '5', '6', '8'],
|
||||
'desc' => '샤프트 사이즈 (인치)'],
|
||||
'options' => ['4', '5', '6', '8'],
|
||||
'desc' => '샤프트 사이즈 (인치)'],
|
||||
|
||||
// 계산 결과
|
||||
['key' => 'calculated_weight', 'name' => '계산 중량', 'type' => 'number', 'required' => false, 'order' => 60,
|
||||
'desc' => '자동 계산된 중량 (kg)'],
|
||||
'desc' => '자동 계산된 중량 (kg)'],
|
||||
|
||||
['key' => 'unit_price', 'name' => '단가', 'type' => 'number', 'required' => false, 'order' => 70,
|
||||
'desc' => '제품 단가 (원)'],
|
||||
'desc' => '제품 단가 (원)'],
|
||||
|
||||
['key' => 'total_price', 'name' => '금액', 'type' => 'number', 'required' => false, 'order' => 71,
|
||||
'desc' => '총 금액 (원)'],
|
||||
'desc' => '총 금액 (원)'],
|
||||
];
|
||||
|
||||
// 6. 스크린 카테고리 필드 생성
|
||||
@@ -256,4 +254,4 @@ public function down(): void
|
||||
DB::table('category_fields')->whereIn('category_id', $categoryIds)->delete();
|
||||
DB::table('categories')->whereIn('id', $categoryIds)->delete();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ public function up(): void
|
||||
$table->decimal('total_amount', 15, 2)->nullable()->comment('총 견적금액');
|
||||
|
||||
$table->enum('status', ['DRAFT', 'SENT', 'APPROVED', 'REJECTED', 'EXPIRED'])
|
||||
->default('DRAFT')->comment('견적 상태');
|
||||
->default('DRAFT')->comment('견적 상태');
|
||||
|
||||
$table->text('notes')->nullable()->comment('비고');
|
||||
$table->date('valid_until')->nullable()->comment('견적 유효기간');
|
||||
@@ -100,4 +100,4 @@ public function down(): void
|
||||
Schema::dropIfExists('estimate_items');
|
||||
Schema::dropIfExists('estimates');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Phase 1: 중요하지 않은 FK 제약조건 제거
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
private function findForeignKeyName(string $table, string $column): ?string
|
||||
{
|
||||
$result = DB::selectOne("
|
||||
$result = DB::selectOne('
|
||||
SELECT CONSTRAINT_NAME
|
||||
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
@@ -28,7 +28,7 @@ private function findForeignKeyName(string $table, string $column): ?string
|
||||
AND COLUMN_NAME = ?
|
||||
AND REFERENCED_TABLE_NAME IS NOT NULL
|
||||
LIMIT 1
|
||||
", [$table, $column]);
|
||||
', [$table, $column]);
|
||||
|
||||
return $result ? $result->CONSTRAINT_NAME : null;
|
||||
}
|
||||
@@ -66,8 +66,8 @@ public function up(): void
|
||||
$classificationIndexExists = DB::selectOne("
|
||||
SHOW INDEX FROM classifications WHERE Column_name = 'tenant_id'
|
||||
");
|
||||
if (!$classificationIndexExists) {
|
||||
DB::statement("CREATE INDEX idx_classifications_tenant_id ON classifications (tenant_id)");
|
||||
if (! $classificationIndexExists) {
|
||||
DB::statement('CREATE INDEX idx_classifications_tenant_id ON classifications (tenant_id)');
|
||||
echo "✅ Added index: classifications.tenant_id\n";
|
||||
} else {
|
||||
echo "ℹ️ Index exists: classifications.tenant_id\n";
|
||||
@@ -77,8 +77,8 @@ public function up(): void
|
||||
$departmentIndexExists = DB::selectOne("
|
||||
SHOW INDEX FROM departments WHERE Column_name = 'parent_id'
|
||||
");
|
||||
if (!$departmentIndexExists) {
|
||||
DB::statement("CREATE INDEX idx_departments_parent_id ON departments (parent_id)");
|
||||
if (! $departmentIndexExists) {
|
||||
DB::statement('CREATE INDEX idx_departments_parent_id ON departments (parent_id)');
|
||||
echo "✅ Added index: departments.parent_id\n";
|
||||
} else {
|
||||
echo "ℹ️ Index exists: departments.parent_id\n";
|
||||
@@ -106,7 +106,7 @@ public function down(): void
|
||||
});
|
||||
echo "✅ Restored FK: departments.parent_id → departments\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo "⚠️ Could not restore FK: departments.parent_id - " . $e->getMessage() . "\n";
|
||||
echo '⚠️ Could not restore FK: departments.parent_id - '.$e->getMessage()."\n";
|
||||
}
|
||||
|
||||
// 2. classifications.tenant_id → tenants FK 복구
|
||||
@@ -121,9 +121,9 @@ public function down(): void
|
||||
});
|
||||
echo "✅ Restored FK: classifications.tenant_id → tenants\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo "⚠️ Could not restore FK: classifications.tenant_id - " . $e->getMessage() . "\n";
|
||||
echo '⚠️ Could not restore FK: classifications.tenant_id - '.$e->getMessage()."\n";
|
||||
}
|
||||
|
||||
echo "\n🔄 Phase 1 FK 복구 완료!\n";
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Phase 2: 견적 시스템 FK 제약조건 제거
|
||||
@@ -22,7 +22,7 @@
|
||||
*/
|
||||
private function findForeignKeyName(string $table, string $column): ?string
|
||||
{
|
||||
$result = DB::selectOne("
|
||||
$result = DB::selectOne('
|
||||
SELECT CONSTRAINT_NAME
|
||||
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
@@ -30,7 +30,7 @@ private function findForeignKeyName(string $table, string $column): ?string
|
||||
AND COLUMN_NAME = ?
|
||||
AND REFERENCED_TABLE_NAME IS NOT NULL
|
||||
LIMIT 1
|
||||
", [$table, $column]);
|
||||
', [$table, $column]);
|
||||
|
||||
return $result ? $result->CONSTRAINT_NAME : null;
|
||||
}
|
||||
@@ -57,8 +57,9 @@ public function up(): void
|
||||
$estimatesExists = Schema::hasTable('estimates');
|
||||
$estimateItemsExists = Schema::hasTable('estimate_items');
|
||||
|
||||
if (!$estimatesExists && !$estimateItemsExists) {
|
||||
if (! $estimatesExists && ! $estimateItemsExists) {
|
||||
echo "ℹ️ 견적 시스템 테이블이 존재하지 않습니다. 건너뜁니다.\n";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,8 +77,8 @@ public function up(): void
|
||||
$indexExists = DB::selectOne("
|
||||
SHOW INDEX FROM estimates WHERE Key_name = 'idx_estimates_tenant_model_set'
|
||||
");
|
||||
if (!$indexExists) {
|
||||
DB::statement("CREATE INDEX idx_estimates_tenant_model_set ON estimates (tenant_id, model_set_id)");
|
||||
if (! $indexExists) {
|
||||
DB::statement('CREATE INDEX idx_estimates_tenant_model_set ON estimates (tenant_id, model_set_id)');
|
||||
echo "✅ Added performance index: estimates(tenant_id, model_set_id)\n";
|
||||
}
|
||||
}
|
||||
@@ -96,8 +97,8 @@ public function up(): void
|
||||
$indexExists = DB::selectOne("
|
||||
SHOW INDEX FROM estimate_items WHERE Key_name = 'idx_estimate_items_tenant_estimate'
|
||||
");
|
||||
if (!$indexExists) {
|
||||
DB::statement("CREATE INDEX idx_estimate_items_tenant_estimate ON estimate_items (tenant_id, estimate_id)");
|
||||
if (! $indexExists) {
|
||||
DB::statement('CREATE INDEX idx_estimate_items_tenant_estimate ON estimate_items (tenant_id, estimate_id)');
|
||||
echo "✅ Added performance index: estimate_items(tenant_id, estimate_id)\n";
|
||||
}
|
||||
}
|
||||
@@ -129,7 +130,7 @@ public function down(): void
|
||||
});
|
||||
echo "✅ Restored FK: estimate_items.estimate_id → estimates\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo "⚠️ Could not restore FK: estimate_items.estimate_id - " . $e->getMessage() . "\n";
|
||||
echo '⚠️ Could not restore FK: estimate_items.estimate_id - '.$e->getMessage()."\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,10 +145,10 @@ public function down(): void
|
||||
});
|
||||
echo "✅ Restored FK: estimates.model_set_id → categories\n";
|
||||
} catch (\Throwable $e) {
|
||||
echo "⚠️ Could not restore FK: estimates.model_set_id - " . $e->getMessage() . "\n";
|
||||
echo '⚠️ Could not restore FK: estimates.model_set_id - '.$e->getMessage()."\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n🔄 Phase 2 FK 복구 완료!\n";
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Phase 3: 제품-자재 관계 FK 제약조건 제거 (신중한 검토 필요)
|
||||
@@ -22,7 +21,7 @@
|
||||
*/
|
||||
private function findForeignKeyName(string $table, string $column): ?string
|
||||
{
|
||||
$result = DB::selectOne("
|
||||
$result = DB::selectOne('
|
||||
SELECT CONSTRAINT_NAME
|
||||
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
@@ -30,7 +29,7 @@ private function findForeignKeyName(string $table, string $column): ?string
|
||||
AND COLUMN_NAME = ?
|
||||
AND REFERENCED_TABLE_NAME IS NOT NULL
|
||||
LIMIT 1
|
||||
", [$table, $column]);
|
||||
', [$table, $column]);
|
||||
|
||||
return $result ? $result->CONSTRAINT_NAME : null;
|
||||
}
|
||||
@@ -55,8 +54,9 @@ public function up(): void
|
||||
echo "📋 분석 범위: BOM 시스템의 자재 참조 관계\n\n";
|
||||
|
||||
// product_components 테이블 존재 여부 확인
|
||||
if (!Schema::hasTable('product_components')) {
|
||||
if (! Schema::hasTable('product_components')) {
|
||||
echo "ℹ️ product_components 테이블이 존재하지 않습니다. 건너뜁니다.\n";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,20 +64,22 @@ public function up(): void
|
||||
|
||||
// 테이블 구조 확인
|
||||
$columns = DB::select('DESCRIBE product_components');
|
||||
$columnNames = array_map(function($col) { return $col->Field; }, $columns);
|
||||
$columnNames = array_map(function ($col) {
|
||||
return $col->Field;
|
||||
}, $columns);
|
||||
|
||||
echo " 현재 테이블 구조:\n";
|
||||
echo " - ref_type 컬럼: " . (in_array('ref_type', $columnNames) ? "존재 (통합 참조 타입)" : "없음") . "\n";
|
||||
echo " - ref_id 컬럼: " . (in_array('ref_id', $columnNames) ? "존재 (통합 참조 ID)" : "없음") . "\n";
|
||||
echo " - material_id 컬럼: " . (in_array('material_id', $columnNames) ? "존재" : "없음 (예상대로)") . "\n\n";
|
||||
echo ' - ref_type 컬럼: '.(in_array('ref_type', $columnNames) ? '존재 (통합 참조 타입)' : '없음')."\n";
|
||||
echo ' - ref_id 컬럼: '.(in_array('ref_id', $columnNames) ? '존재 (통합 참조 ID)' : '없음')."\n";
|
||||
echo ' - material_id 컬럼: '.(in_array('material_id', $columnNames) ? '존재' : '없음 (예상대로)')."\n\n";
|
||||
|
||||
// 현재 FK 상태 확인
|
||||
$parentProductFk = $this->findForeignKeyName('product_components', 'parent_product_id');
|
||||
$refIdFk = $this->findForeignKeyName('product_components', 'ref_id');
|
||||
|
||||
echo "2️⃣ FK 제약조건 상태 확인...\n";
|
||||
echo " - parent_product_id FK: " . ($parentProductFk ? "존재 ({$parentProductFk})" : "없음") . "\n";
|
||||
echo " - ref_id FK: " . ($refIdFk ? "존재 ({$refIdFk})" : "없음 (유연한 구조)") . "\n\n";
|
||||
echo ' - parent_product_id FK: '.($parentProductFk ? "존재 ({$parentProductFk})" : '없음')."\n";
|
||||
echo ' - ref_id FK: '.($refIdFk ? "존재 ({$refIdFk})" : '없음 (유연한 구조)')."\n\n";
|
||||
|
||||
// 성능을 위한 인덱스 확인
|
||||
echo "3️⃣ 성능 인덱스 상태 확인...\n";
|
||||
@@ -88,14 +90,14 @@ public function up(): void
|
||||
SHOW INDEX FROM product_components WHERE Key_name LIKE '%ref_id%' OR Column_name = 'ref_id'
|
||||
");
|
||||
|
||||
echo " - ref_type 인덱스: " . ($refTypeIndexExists ? "존재" : "없음") . "\n";
|
||||
echo " - ref_id 인덱스: " . ($refIdIndexExists ? "존재" : "없음") . "\n";
|
||||
echo ' - ref_type 인덱스: '.($refTypeIndexExists ? '존재' : '없음')."\n";
|
||||
echo ' - ref_id 인덱스: '.($refIdIndexExists ? '존재' : '없음')."\n";
|
||||
|
||||
// 필요시 성능 인덱스 추가
|
||||
if (!$refTypeIndexExists || !$refIdIndexExists) {
|
||||
if (! $refTypeIndexExists || ! $refIdIndexExists) {
|
||||
echo "\n4️⃣ 성능 인덱스 추가...\n";
|
||||
try {
|
||||
DB::statement("CREATE INDEX idx_components_ref_type_id ON product_components (ref_type, ref_id)");
|
||||
DB::statement('CREATE INDEX idx_components_ref_type_id ON product_components (ref_type, ref_id)');
|
||||
echo "✅ Added composite index: product_components(ref_type, ref_id)\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "ℹ️ Index may already exist or not needed\n";
|
||||
@@ -122,7 +124,7 @@ public function down(): void
|
||||
if (Schema::hasTable('product_components')) {
|
||||
echo "1️⃣ 성능 인덱스 제거...\n";
|
||||
try {
|
||||
DB::statement("DROP INDEX idx_components_ref_type_id ON product_components");
|
||||
DB::statement('DROP INDEX idx_components_ref_type_id ON product_components');
|
||||
echo "✅ Removed index: product_components(ref_type, ref_id)\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "ℹ️ Index may not exist or already removed\n";
|
||||
@@ -132,4 +134,4 @@ public function down(): void
|
||||
echo "\n🔄 Phase 3 분석 롤백 완료!\n";
|
||||
echo "📝 참고: 원래 ref_type/ref_id 구조가 복구되었습니다.\n";
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user