feat(item-master): 잠금 기능 추가 및 FK 레거시 코드 정리

## 잠금 기능 (Lock Feature)
- entity_relationships 테이블에 is_locked, locked_by, locked_at 컬럼 추가
- EntityRelationship 모델에 잠금 관련 헬퍼 메서드 추가
- LockCheckTrait 생성 (destroy 시 잠금 체크 공통 로직)
- 각 Service의 destroy() 메서드에 잠금 체크 적용
- API 응답에 is_locked 필드 포함
- 한국어 에러 메시지 추가

## FK 레거시 코드 정리
- ItemMasterSeeder: entity_relationships 기반으로 전환
- ItemPage 모델: FK 기반 sections() 관계 제거
- ItemSectionService: clone() 메서드 FK 제거
- SectionTemplateService: page_id 컬럼 참조 제거
- EntityRelationship::link() 파라미터 순서 통일

## 기타
- Swagger 스키마에 is_locked 속성 추가
- 프론트엔드 가이드 문서 추가
This commit is contained in:
2025-11-27 15:51:00 +09:00
parent 29fe1415e5
commit efa2a84d2c
35 changed files with 537 additions and 142 deletions

View File

@@ -2,15 +2,15 @@
namespace Database\Seeders;
use App\Models\ItemMaster\UnitOption;
use App\Models\ItemMaster\SectionTemplate;
use App\Models\ItemMaster\ItemMasterField;
use App\Models\ItemMaster\CustomTab;
use App\Models\ItemMaster\EntityRelationship;
use App\Models\ItemMaster\ItemBomItem;
use App\Models\ItemMaster\ItemField;
use App\Models\ItemMaster\ItemPage;
use App\Models\ItemMaster\ItemSection;
use App\Models\ItemMaster\ItemField;
use App\Models\ItemMaster\ItemBomItem;
use App\Models\ItemMaster\CustomTab;
use App\Models\ItemMaster\SectionTemplate;
use App\Models\ItemMaster\TabColumn;
use App\Models\ItemMaster\UnitOption;
use Illuminate\Database\Seeder;
class ItemMasterSeeder extends Seeder
@@ -22,6 +22,7 @@ public function run(): void
{
$tenantId = 1; // 기본 테넌트 ID (실제 환경에 맞게 조정)
$userId = 1; // 기본 사용자 ID (실제 환경에 맞게 조정)
$groupId = 1; // 기본 그룹 ID
// 1. 단위 옵션
$units = [
@@ -60,7 +61,7 @@ public function run(): void
]);
}
// 3. 마스터 필드
// 3. 마스터 필드 (독립 엔티티로 생성)
$masterFields = [
['field_name' => '제품명', 'field_type' => 'textbox', 'category' => '기본정보', 'is_common' => true],
['field_name' => '제품코드', 'field_type' => 'textbox', 'category' => '기본정보', 'is_common' => true],
@@ -77,10 +78,13 @@ public function run(): void
];
foreach ($masterFields as $field) {
ItemMasterField::create([
ItemField::create([
'tenant_id' => $tenantId,
'group_id' => $groupId,
'field_name' => $field['field_name'],
'field_type' => $field['field_type'],
'order_no' => 0,
'is_required' => false,
'category' => $field['category'],
'is_common' => $field['is_common'],
'options' => $field['options'] ?? null,
@@ -99,8 +103,10 @@ public function run(): void
];
foreach ($pages as $index => $page) {
// 페이지 생성 (독립 엔티티)
$itemPage = ItemPage::create([
'tenant_id' => $tenantId,
'group_id' => $groupId,
'page_name' => $page['page_name'],
'item_type' => $page['item_type'],
'absolute_path' => $page['absolute_path'],
@@ -108,65 +114,89 @@ public function run(): void
'created_by' => $userId,
]);
// 각 페이지에 기본 섹션 추가
// 기본 정보 섹션 생성 (독립 엔티티)
$section1 = ItemSection::create([
'tenant_id' => $tenantId,
'page_id' => $itemPage->id,
'group_id' => $groupId,
'title' => '기본 정보',
'type' => 'fields',
'order_no' => 0,
'created_by' => $userId,
]);
// 섹션에 필드 추가
ItemField::create([
'tenant_id' => $tenantId,
'section_id' => $section1->id,
'field_name' => '제품명',
'field_type' => 'textbox',
'order_no' => 0,
'is_required' => true,
'placeholder' => '제품명을 입력하세요',
'created_by' => $userId,
]);
// 페이지-섹션 관계 생성 (entity_relationships)
// link(tenantId, parentType, parentId, childType, childId, orderNo, metadata, groupId)
EntityRelationship::link(
$tenantId,
EntityRelationship::TYPE_PAGE,
$itemPage->id,
EntityRelationship::TYPE_SECTION,
$section1->id,
0, // order_no
null, // metadata
$groupId
);
ItemField::create([
'tenant_id' => $tenantId,
'section_id' => $section1->id,
'field_name' => '제품코드',
'field_type' => 'textbox',
'order_no' => 1,
'is_required' => true,
'placeholder' => '제품코드를 입력하세요',
'created_by' => $userId,
]);
// 필드 생성 및 섹션에 연결
$fieldData = [
['field_name' => '제품명', 'is_required' => true, 'placeholder' => '제품명을 입력하세요'],
['field_name' => '제품코드', 'is_required' => true, 'placeholder' => '제품코드를 입력하세요'],
['field_name' => '규격', 'is_required' => false, 'placeholder' => '규격을 입력하세요'],
];
ItemField::create([
'tenant_id' => $tenantId,
'section_id' => $section1->id,
'field_name' => '규격',
'field_type' => 'textbox',
'order_no' => 2,
'is_required' => false,
'placeholder' => '규격을 입력하세요',
'created_by' => $userId,
]);
foreach ($fieldData as $orderNo => $fd) {
$field = ItemField::create([
'tenant_id' => $tenantId,
'group_id' => $groupId,
'field_name' => $fd['field_name'],
'field_type' => 'textbox',
'order_no' => $orderNo,
'is_required' => $fd['is_required'],
'placeholder' => $fd['placeholder'],
'created_by' => $userId,
]);
// 섹션-필드 관계 생성 (entity_relationships)
EntityRelationship::link(
$tenantId,
EntityRelationship::TYPE_SECTION,
$section1->id,
EntityRelationship::TYPE_FIELD,
$field->id,
$orderNo,
null,
$groupId
);
}
// BOM 섹션 (완제품, 반제품만)
if (in_array($page['item_type'], ['FG', 'PT'])) {
// BOM 섹션 생성 (독립 엔티티)
$section2 = ItemSection::create([
'tenant_id' => $tenantId,
'page_id' => $itemPage->id,
'group_id' => $groupId,
'title' => 'BOM 구성',
'type' => 'bom',
'order_no' => 1,
'created_by' => $userId,
]);
// BOM 항목 샘플
ItemBomItem::create([
// 페이지-섹션 관계 생성
EntityRelationship::link(
$tenantId,
EntityRelationship::TYPE_PAGE,
$itemPage->id,
EntityRelationship::TYPE_SECTION,
$section2->id,
1, // order_no
null,
$groupId
);
// BOM 항목 생성 (독립 엔티티)
$bomItem = ItemBomItem::create([
'tenant_id' => $tenantId,
'section_id' => $section2->id,
'group_id' => $groupId,
'item_code' => 'MAT-001',
'item_name' => '철판',
'quantity' => 10.5,
@@ -177,6 +207,18 @@ public function run(): void
'note' => '샘플 BOM 항목',
'created_by' => $userId,
]);
// 섹션-BOM 관계 생성
EntityRelationship::link(
$tenantId,
EntityRelationship::TYPE_SECTION,
$section2->id,
EntityRelationship::TYPE_BOM,
$bomItem->id,
0,
null,
$groupId
);
}
}
@@ -212,10 +254,10 @@ public function run(): void
}
echo "✅ ItemMaster 시드 데이터 생성 완료!\n";
echo " - 단위 옵션: ".count($units)."\n";
echo " - 섹션 템플릿: ".count($templates)."\n";
echo " - 마스터 필드: ".count($masterFields)."\n";
echo " - 품목 페이지: ".count($pages)."\n";
echo " - 커스텀 탭: ".count($tabs)."\n";
echo ' - 단위 옵션: '.count($units)."\n";
echo ' - 섹션 템플릿: '.count($templates)."\n";
echo ' - 마스터 필드: '.count($masterFields)."\n";
echo ' - 품목 페이지: '.count($pages)."\n";
echo ' - 커스텀 탭: '.count($tabs)."\n";
}
}