From 90bcfaf26852a01109702daf79ac8b547d91ddd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 29 Jan 2026 01:12:40 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=EB=85=BC=EB=A6=AC=EC=A0=81=20?= =?UTF-8?q?=EA=B4=80=EA=B3=84=20=EB=AC=B8=EC=84=9C=20=EB=B0=8F=20=EA=B8=80?= =?UTF-8?q?=EB=A1=9C=EB=B2=8C=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LOGICAL_RELATIONSHIPS.md 업데이트 - create_global_categories_table 마이그레이션 추가 Co-Authored-By: Claude Opus 4.5 --- LOGICAL_RELATIONSHIPS.md | 31 ++++- ..._152523_create_global_categories_table.php | 118 ++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2026_01_27_152523_create_global_categories_table.php diff --git a/LOGICAL_RELATIONSHIPS.md b/LOGICAL_RELATIONSHIPS.md index 575494d..12cf164 100644 --- a/LOGICAL_RELATIONSHIPS.md +++ b/LOGICAL_RELATIONSHIPS.md @@ -1,6 +1,6 @@ # 논리적 데이터베이스 관계 문서 -> **자동 생성**: 2026-01-26 22:07:37 +> **자동 생성**: 2026-01-29 00:51:16 > **소스**: Eloquent 모델 관계 분석 ## 📊 모델별 관계 현황 @@ -194,6 +194,35 @@ ### model_versions - **model()**: belongsTo → `models` - **bomTemplates()**: hasMany → `bom_templates` +### documents +**모델**: `App\Models\Documents\Document` + +- **creator()**: belongsTo → `users` +- **updater()**: belongsTo → `users` +- **approvals()**: hasMany → `document_approvals` +- **data()**: hasMany → `document_data` +- **attachments()**: hasMany → `document_attachments` +- **linkable()**: morphTo → `(Polymorphic)` + +### document_approvals +**모델**: `App\Models\Documents\DocumentApproval` + +- **document()**: belongsTo → `documents` +- **user()**: belongsTo → `users` +- **creator()**: belongsTo → `users` + +### document_attachments +**모델**: `App\Models\Documents\DocumentAttachment` + +- **document()**: belongsTo → `documents` +- **file()**: belongsTo → `files` +- **creator()**: belongsTo → `users` + +### document_datas +**모델**: `App\Models\Documents\DocumentData` + +- **document()**: belongsTo → `documents` + ### estimates **모델**: `App\Models\Estimate\Estimate` diff --git a/database/migrations/2026_01_27_152523_create_global_categories_table.php b/database/migrations/2026_01_27_152523_create_global_categories_table.php new file mode 100644 index 0000000..28baa8f --- /dev/null +++ b/database/migrations/2026_01_27_152523_create_global_categories_table.php @@ -0,0 +1,118 @@ +id()->comment('글로벌 카테고리 PK'); + $table->unsignedBigInteger('parent_id')->nullable()->comment('상위 카테고리ID(NULL=최상위)'); + $table->string('code_group', 30)->comment('코드 그룹'); + $table->string('profile_code', 30)->nullable()->comment('역할 프로필 코드'); + $table->string('code', 30)->comment('카테고리 코드(영문, 고유)'); + $table->string('name', 100)->comment('카테고리명'); + $table->boolean('is_active')->default(true)->comment('활성여부'); + $table->integer('sort_order')->default(1)->comment('정렬순서'); + $table->string('description', 255)->nullable()->comment('비고'); + $table->unsignedBigInteger('created_by')->nullable()->comment('등록자'); + $table->unsignedBigInteger('updated_by')->nullable()->comment('수정자'); + $table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자'); + $table->timestamps(); + $table->softDeletes(); + + // 인덱스 + $table->unique(['code_group', 'code'], 'uq_global_codegroup_code'); + $table->index('parent_id', 'idx_global_parent_id'); + $table->index('code_group', 'idx_global_code_group'); + + // 자기 참조 FK + $table->foreign('parent_id', 'fk_global_category_parent') + ->references('id') + ->on('global_categories') + ->onDelete('set null'); + }); + + // tenant 1의 카테고리를 글로벌 카테고리로 복사 + $this->seedFromTenant1(); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('global_categories'); + } + + /** + * tenant 1의 카테고리를 글로벌로 복사 + */ + private function seedFromTenant1(): void + { + $categories = \DB::table('categories') + ->where('tenant_id', 1) + ->whereNull('deleted_at') + ->orderBy('parent_id') + ->orderBy('sort_order') + ->get(); + + if ($categories->isEmpty()) { + return; + } + + $idMap = []; // old_id => new_id + + // 1단계: parent_id가 NULL인 루트 카테고리 + foreach ($categories->where('parent_id', null) as $cat) { + $newId = \DB::table('global_categories')->insertGetId([ + 'parent_id' => null, + 'code_group' => $cat->code_group, + 'profile_code' => $cat->profile_code, + 'code' => $cat->code, + 'name' => $cat->name, + 'is_active' => $cat->is_active, + 'sort_order' => $cat->sort_order, + 'description' => $cat->description, + 'created_at' => now(), + 'updated_at' => now(), + ]); + $idMap[$cat->id] = $newId; + } + + // 2단계: 자식 카테고리 (재귀적으로 처리) + $remaining = $categories->whereNotNull('parent_id'); + $maxIterations = 10; + $iteration = 0; + + while ($remaining->isNotEmpty() && $iteration < $maxIterations) { + $processed = []; + foreach ($remaining as $cat) { + if (isset($idMap[$cat->parent_id])) { + $newId = \DB::table('global_categories')->insertGetId([ + 'parent_id' => $idMap[$cat->parent_id], + 'code_group' => $cat->code_group, + 'profile_code' => $cat->profile_code, + 'code' => $cat->code, + 'name' => $cat->name, + 'is_active' => $cat->is_active, + 'sort_order' => $cat->sort_order, + 'description' => $cat->description, + 'created_at' => now(), + 'updated_at' => now(), + ]); + $idMap[$cat->id] = $newId; + $processed[] = $cat->id; + } + } + $remaining = $remaining->whereNotIn('id', $processed); + $iteration++; + } + } +};