docs: Items BOM 테스트 및 테이블 통합 계획 추가
- Items BOM API 플로우 테스트 JSON 추가 (items-bom-test.json)
- GET /items/{id} BOM 확장 데이터 검증
- GET /items/{id}/bom, /items/{id}/bom/tree 엔드포인트 테스트
- Items 테이블 통합 계획 문서 추가 (items-table-unification-plan.md)
- Products/Materials → Items 통합 마이그레이션 설계
- MNG 필드 관리 계획 필수 참조 문서 섹션 보강
This commit is contained in:
108
plans/flow-tests/items-bom-test.json
Normal file
108
plans/flow-tests/items-bom-test.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"name": "Items BOM 데이터 조회 테스트",
|
||||
"description": "GET /items/{id} 호출 시 BOM 데이터가 확장되어 반환되는지 테스트 (child_item_code, child_item_name, unit 포함)",
|
||||
"version": "1.0",
|
||||
"config": {
|
||||
"baseUrl": "",
|
||||
"timeout": 30000,
|
||||
"stopOnFailure": true
|
||||
},
|
||||
"variables": {
|
||||
"user_id": "{{$env.FLOW_TESTER_USER_ID}}",
|
||||
"user_pwd": "{{$env.FLOW_TESTER_USER_PWD}}",
|
||||
"test_item_id": "818",
|
||||
"test_item_type": "FG"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"id": "login",
|
||||
"name": "로그인",
|
||||
"method": "POST",
|
||||
"endpoint": "/login",
|
||||
"body": {
|
||||
"user_id": "{{user_id}}",
|
||||
"user_pwd": "{{user_pwd}}"
|
||||
},
|
||||
"expect": {
|
||||
"status": [200],
|
||||
"jsonPath": {
|
||||
"$.access_token": "@isString"
|
||||
}
|
||||
},
|
||||
"extract": {
|
||||
"token": "$.access_token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "get_items_list",
|
||||
"name": "품목 목록 조회 (FG/PT 타입)",
|
||||
"method": "GET",
|
||||
"endpoint": "/items?type=FG,PT&size=10",
|
||||
"headers": {
|
||||
"Authorization": "Bearer {{login.token}}"
|
||||
},
|
||||
"expect": {
|
||||
"status": [200],
|
||||
"jsonPath": {
|
||||
"$.success": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "get_item_detail",
|
||||
"name": "품목 상세 조회 (BOM 확장 확인)",
|
||||
"method": "GET",
|
||||
"endpoint": "/items/{{test_item_id}}?item_type={{test_item_type}}",
|
||||
"headers": {
|
||||
"Authorization": "Bearer {{login.token}}"
|
||||
},
|
||||
"expect": {
|
||||
"status": [200],
|
||||
"jsonPath": {
|
||||
"$.success": true,
|
||||
"$.data.id": "@isNumber",
|
||||
"$.data.item_type": "@isString",
|
||||
"$.data.code": "@isString",
|
||||
"$.data.name": "@isString"
|
||||
}
|
||||
},
|
||||
"extract": {
|
||||
"item_id": "$.data.id",
|
||||
"item_code": "$.data.code",
|
||||
"item_bom": "$.data.bom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "get_item_bom_api",
|
||||
"name": "Items BOM API 조회",
|
||||
"description": "GET /items/{id}/bom 엔드포인트 테스트",
|
||||
"method": "GET",
|
||||
"endpoint": "/items/{{test_item_id}}/bom",
|
||||
"headers": {
|
||||
"Authorization": "Bearer {{login.token}}"
|
||||
},
|
||||
"expect": {
|
||||
"status": [200],
|
||||
"jsonPath": {
|
||||
"$.success": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "get_item_bom_tree",
|
||||
"name": "Items BOM 트리 조회",
|
||||
"description": "GET /items/{id}/bom/tree 엔드포인트 테스트",
|
||||
"method": "GET",
|
||||
"endpoint": "/items/{{test_item_id}}/bom/tree",
|
||||
"headers": {
|
||||
"Authorization": "Bearer {{login.token}}"
|
||||
},
|
||||
"expect": {
|
||||
"status": [200],
|
||||
"jsonPath": {
|
||||
"$.success": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
550
plans/items-table-unification-plan.md
Normal file
550
plans/items-table-unification-plan.md
Normal file
@@ -0,0 +1,550 @@
|
||||
# Items 테이블 통합 마이그레이션 계획
|
||||
|
||||
## 개요
|
||||
|
||||
### 목적
|
||||
현재 이원화된 `products`/`materials` 테이블을 통합 `items` 테이블 구조로 전환하여:
|
||||
- BOM 관리 시 `child_item_type` 불필요 (ID만으로 유일 식별)
|
||||
- 단일 쿼리로 모든 품목 조회 가능
|
||||
- Service/Controller 코드 50% 감소
|
||||
- 유지보수성 및 확장성 향상
|
||||
|
||||
### 설계 변경 이력
|
||||
- **2025-12-11 (v1)**: 단일 `items` 테이블에 모든 필드 통합
|
||||
- **2025-12-11 (v2)**: 4개 테이블로 분리하여 성능 최적화
|
||||
- `items`: 핵심 필드 (목록 조회, BOM 계산)
|
||||
- `item_details`: 필수/인덱싱 필드 (검색, 필터링)
|
||||
- `item_attributes`: 동적 속성 (JSON 기반)
|
||||
- `item_bending`: 절곡 정보 (별도 관리)
|
||||
|
||||
### 현재 상태 분석
|
||||
|
||||
#### 테이블 구조 비교
|
||||
|
||||
| 구분 | Products | Materials | 통합 Items |
|
||||
|------|----------|-----------|------------|
|
||||
| ID | id (1~808) | id (1~417) | id (새 통합 ID) |
|
||||
| 타입 | product_type (FG, PT, PRODUCT, SUBASSEMBLY, PART, CS) | material_type (SM, RM, CS) | item_type |
|
||||
| 코드 | code | material_code | code |
|
||||
| 이름 | name | name | name |
|
||||
| 단위 | unit | unit | unit |
|
||||
| 규격 | - | specification | attributes (JSON) |
|
||||
| BOM | bom (JSON) | - | bom (JSON) |
|
||||
| 속성 | attributes, options | attributes, options | attributes, options |
|
||||
|
||||
#### 데이터 현황 (2025-12-11)
|
||||
- Products: **808건**
|
||||
- Materials: **417건**
|
||||
- 총합: **1,225건**
|
||||
|
||||
#### 참조 테이블 현황
|
||||
|
||||
| 테이블 | 참조 방식 | 데이터 |
|
||||
|--------|----------|--------|
|
||||
| product_components | parent_product_id, ref_type+ref_id | 16건 |
|
||||
| bom_template_items | ref_type+ref_id | 1건 |
|
||||
| orders | product_id | 0건 |
|
||||
| order_items | product_id | 0건 |
|
||||
| quotes | product_id | 0건 |
|
||||
| material_receipts | material_id | 0건 |
|
||||
| lots | material_id | 0건 |
|
||||
| price_histories | item_type+item_id | 조회 필요 |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 테이블 생성 및 데이터 이관
|
||||
|
||||
### 1.1 items 테이블 (핵심 정보)
|
||||
|
||||
```sql
|
||||
CREATE TABLE items (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
|
||||
|
||||
-- 기본 정보
|
||||
item_type VARCHAR(15) NOT NULL COMMENT '품목 유형: FG, PT, SM, RM, CS, PRODUCT, SUBASSEMBLY, PART',
|
||||
code VARCHAR(100) NOT NULL COMMENT '품목 코드',
|
||||
name VARCHAR(255) NOT NULL COMMENT '품목명',
|
||||
unit VARCHAR(20) NULL COMMENT '단위',
|
||||
category_id BIGINT UNSIGNED NULL COMMENT '카테고리 ID',
|
||||
|
||||
-- BOM
|
||||
bom JSON NULL COMMENT 'BOM [{child_item_id, quantity}, ...]',
|
||||
|
||||
-- 상태
|
||||
is_active TINYINT(1) DEFAULT 1 COMMENT '활성 상태',
|
||||
|
||||
-- 레거시 참조 (전환기간용)
|
||||
legacy_table VARCHAR(20) NULL COMMENT '원본 테이블: products | materials',
|
||||
legacy_id BIGINT UNSIGNED NULL COMMENT '원본 테이블 ID',
|
||||
|
||||
-- 감사 필드
|
||||
created_by BIGINT UNSIGNED NULL COMMENT '생성자',
|
||||
updated_by BIGINT UNSIGNED NULL COMMENT '수정자',
|
||||
deleted_by BIGINT UNSIGNED NULL COMMENT '삭제자',
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
deleted_at TIMESTAMP NULL,
|
||||
|
||||
-- 인덱스
|
||||
INDEX idx_items_tenant_type (tenant_id, item_type),
|
||||
INDEX idx_items_tenant_code (tenant_id, code),
|
||||
INDEX idx_items_tenant_category (tenant_id, category_id),
|
||||
INDEX idx_items_tenant_active (tenant_id, is_active),
|
||||
INDEX idx_items_legacy (legacy_table, legacy_id),
|
||||
UNIQUE KEY uq_items_tenant_code (tenant_id, code, deleted_at),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id),
|
||||
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='Products + Materials 통합 품목 테이블 (핵심 정보)';
|
||||
```
|
||||
|
||||
### 1.2 item_details 테이블 (필수/인덱싱 필드)
|
||||
|
||||
```sql
|
||||
CREATE TABLE item_details (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
item_id BIGINT UNSIGNED NOT NULL COMMENT 'items 테이블 FK',
|
||||
|
||||
-- Products 전용 (인덱싱/필터링)
|
||||
is_sellable TINYINT(1) DEFAULT 1 COMMENT '판매 가능',
|
||||
is_purchasable TINYINT(1) DEFAULT 0 COMMENT '구매 가능',
|
||||
is_producible TINYINT(1) DEFAULT 0 COMMENT '생산 가능',
|
||||
safety_stock INT NULL COMMENT '안전 재고',
|
||||
lead_time INT NULL COMMENT '리드타임 (일)',
|
||||
is_variable_size TINYINT(1) DEFAULT 0 COMMENT '가변 사이즈 여부',
|
||||
product_category VARCHAR(50) NULL COMMENT '제품 카테고리',
|
||||
part_type VARCHAR(50) NULL COMMENT '부품 유형',
|
||||
|
||||
-- Materials 전용 (인덱싱/필터링)
|
||||
is_inspection VARCHAR(1) DEFAULT 'N' COMMENT '검사 대상 여부',
|
||||
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
|
||||
-- 인덱스
|
||||
UNIQUE KEY uq_item_details_item_id (item_id),
|
||||
INDEX idx_item_details_sellable (is_sellable),
|
||||
INDEX idx_item_details_purchasable (is_purchasable),
|
||||
INDEX idx_item_details_producible (is_producible),
|
||||
INDEX idx_item_details_inspection (is_inspection),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='품목 상세 정보 (필수/인덱싱 필드)';
|
||||
```
|
||||
|
||||
### 1.3 item_attributes 테이블 (동적 속성)
|
||||
|
||||
```sql
|
||||
CREATE TABLE item_attributes (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
item_id BIGINT UNSIGNED NOT NULL COMMENT 'items 테이블 FK',
|
||||
|
||||
-- 동적 속성
|
||||
attributes JSON NULL COMMENT '동적 속성 (규격 정보 포함)',
|
||||
options JSON NULL COMMENT '옵션 (파일, 인증, 설명 등)',
|
||||
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
|
||||
-- 인덱스
|
||||
UNIQUE KEY uq_item_attributes_item_id (item_id),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='품목 동적 속성 (JSON 기반)';
|
||||
```
|
||||
|
||||
### 1.4 item_bending 테이블 (절곡 정보)
|
||||
|
||||
```sql
|
||||
CREATE TABLE item_bending (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
item_id BIGINT UNSIGNED NOT NULL COMMENT 'items 테이블 FK',
|
||||
|
||||
-- 절곡 정보
|
||||
diagram VARCHAR(255) NULL COMMENT '절곡 도면 파일',
|
||||
details JSON NULL COMMENT '절곡 상세 정보',
|
||||
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
|
||||
-- 인덱스
|
||||
UNIQUE KEY uq_item_bending_item_id (item_id),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='품목 절곡 정보';
|
||||
```
|
||||
|
||||
### 1.5 item_id_mappings 테이블 (전환기간용)
|
||||
|
||||
```sql
|
||||
CREATE TABLE item_id_mappings (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
new_item_id BIGINT UNSIGNED NOT NULL COMMENT '새 items 테이블 ID',
|
||||
legacy_table VARCHAR(20) NOT NULL COMMENT '원본 테이블: products | materials',
|
||||
legacy_id BIGINT UNSIGNED NOT NULL COMMENT '원본 테이블 ID',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 인덱스
|
||||
UNIQUE KEY uq_legacy (legacy_table, legacy_id),
|
||||
INDEX idx_new_item (new_item_id),
|
||||
|
||||
-- 외래키
|
||||
FOREIGN KEY (new_item_id) REFERENCES items(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='Products/Materials → Items ID 매핑 (전환기간용)';
|
||||
```
|
||||
|
||||
### 1.6 데이터 이관 스크립트
|
||||
|
||||
```php
|
||||
// 1. Products → Items 이관
|
||||
DB::statement("
|
||||
INSERT INTO items (
|
||||
tenant_id, item_type, code, name, unit, category_id, bom,
|
||||
is_active, created_by, updated_by, deleted_by,
|
||||
created_at, updated_at, deleted_at,
|
||||
legacy_table, legacy_id
|
||||
)
|
||||
SELECT
|
||||
tenant_id, product_type, code, name, unit, category_id, bom,
|
||||
is_active, created_by, updated_by, deleted_by,
|
||||
created_at, updated_at, deleted_at,
|
||||
'products', id
|
||||
FROM products
|
||||
");
|
||||
|
||||
// 2. Products → item_details 이관
|
||||
DB::statement("
|
||||
INSERT INTO item_details (
|
||||
item_id, is_sellable, is_purchasable, is_producible,
|
||||
safety_stock, lead_time, is_variable_size,
|
||||
product_category, part_type, is_inspection,
|
||||
created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
i.id, p.is_sellable, p.is_purchasable, p.is_producible,
|
||||
p.safety_stock, p.lead_time, p.is_variable_size,
|
||||
p.product_category, p.part_type, 'N',
|
||||
p.created_at, p.updated_at
|
||||
FROM products p
|
||||
JOIN items i ON i.legacy_table = 'products' AND i.legacy_id = p.id
|
||||
");
|
||||
|
||||
// 3. Products → item_attributes 이관
|
||||
DB::statement("
|
||||
INSERT INTO item_attributes (
|
||||
item_id, attributes, options, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
i.id, p.attributes, p.options, p.created_at, p.updated_at
|
||||
FROM products p
|
||||
JOIN items i ON i.legacy_table = 'products' AND i.legacy_id = p.id
|
||||
");
|
||||
|
||||
// 4. Products → item_bending 이관 (절곡 정보 있는 경우만)
|
||||
DB::statement("
|
||||
INSERT INTO item_bending (
|
||||
item_id, diagram, details, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
i.id, p.bending_diagram, p.bending_details, p.created_at, p.updated_at
|
||||
FROM products p
|
||||
JOIN items i ON i.legacy_table = 'products' AND i.legacy_id = p.id
|
||||
WHERE p.bending_diagram IS NOT NULL OR p.bending_details IS NOT NULL
|
||||
");
|
||||
|
||||
// 5. Materials → Items 이관
|
||||
DB::statement("
|
||||
INSERT INTO items (
|
||||
tenant_id, item_type, code, name, unit, category_id,
|
||||
is_active, created_by, updated_by, deleted_by,
|
||||
created_at, updated_at, deleted_at,
|
||||
legacy_table, legacy_id
|
||||
)
|
||||
SELECT
|
||||
tenant_id, material_type, material_code, name, unit, category_id,
|
||||
is_active, created_by, updated_by, deleted_by,
|
||||
created_at, updated_at, deleted_at,
|
||||
'materials', id
|
||||
FROM materials
|
||||
");
|
||||
|
||||
// 6. Materials → item_details 이관
|
||||
DB::statement("
|
||||
INSERT INTO item_details (
|
||||
item_id, is_sellable, is_purchasable, is_producible,
|
||||
safety_stock, lead_time, is_variable_size,
|
||||
product_category, part_type, is_inspection,
|
||||
created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
i.id, 0, 1, 0,
|
||||
NULL, NULL, 0,
|
||||
NULL, NULL, m.is_inspection,
|
||||
m.created_at, m.updated_at
|
||||
FROM materials m
|
||||
JOIN items i ON i.legacy_table = 'materials' AND i.legacy_id = m.id
|
||||
");
|
||||
|
||||
// 7. Materials → item_attributes 이관
|
||||
// options에 기존 Materials 전용 필드 포함
|
||||
DB::statement("
|
||||
INSERT INTO item_attributes (
|
||||
item_id, attributes, options, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
i.id,
|
||||
m.attributes,
|
||||
JSON_OBJECT(
|
||||
'specification', m.specification,
|
||||
'item_name', m.item_name,
|
||||
'search_tag', m.search_tag,
|
||||
'remarks', m.remarks
|
||||
),
|
||||
m.created_at, m.updated_at
|
||||
FROM materials m
|
||||
JOIN items i ON i.legacy_table = 'materials' AND i.legacy_id = m.id
|
||||
");
|
||||
|
||||
// 8. ID 매핑 테이블 생성
|
||||
DB::statement("
|
||||
INSERT INTO item_id_mappings (new_item_id, legacy_table, legacy_id)
|
||||
SELECT id, legacy_table, legacy_id
|
||||
FROM items
|
||||
WHERE legacy_table IS NOT NULL AND legacy_id IS NOT NULL
|
||||
");
|
||||
```
|
||||
|
||||
### 1.7 체크리스트
|
||||
|
||||
- [ ] items 테이블 마이그레이션 생성
|
||||
- [ ] item_details 테이블 마이그레이션 생성
|
||||
- [ ] item_attributes 테이블 마이그레이션 생성
|
||||
- [ ] item_bending 테이블 마이그레이션 생성
|
||||
- [ ] item_id_mappings 테이블 마이그레이션 생성
|
||||
- [ ] Products 데이터 이관 스크립트 실행
|
||||
- [ ] Materials 데이터 이관 스크립트 실행
|
||||
- [ ] 데이터 검증 (건수, 필드값 일치)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Item 모델 및 Service 생성
|
||||
|
||||
### 2.1 Item 모델
|
||||
|
||||
```php
|
||||
// app/Models/Items/Item.php
|
||||
class Item extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id', 'item_type', 'code', 'name', 'unit', 'category_id',
|
||||
'bom', 'is_active', 'created_by', 'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'bom' => 'array',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
// 1:1 관계
|
||||
public function details() { return $this->hasOne(ItemDetail::class); }
|
||||
public function attributes() { return $this->hasOne(ItemAttribute::class); }
|
||||
public function bending() { return $this->hasOne(ItemBending::class); }
|
||||
|
||||
// 타입별 스코프
|
||||
public function scopeType($q, string $type) { return $q->where('item_type', $type); }
|
||||
public function scopeProducts($q) { return $q->whereIn('item_type', ['FG', 'PT', 'PRODUCT', 'SUBASSEMBLY', 'PART']); }
|
||||
public function scopeMaterials($q) { return $q->whereIn('item_type', ['SM', 'RM', 'CS']); }
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 관련 모델
|
||||
|
||||
```php
|
||||
// app/Models/Items/ItemDetail.php
|
||||
class ItemDetail extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'item_id', 'is_sellable', 'is_purchasable', 'is_producible',
|
||||
'safety_stock', 'lead_time', 'is_variable_size',
|
||||
'product_category', 'part_type', 'is_inspection',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_sellable' => 'boolean',
|
||||
'is_purchasable' => 'boolean',
|
||||
'is_producible' => 'boolean',
|
||||
'is_variable_size' => 'boolean',
|
||||
];
|
||||
|
||||
public function item() { return $this->belongsTo(Item::class); }
|
||||
}
|
||||
|
||||
// app/Models/Items/ItemAttribute.php
|
||||
class ItemAttribute extends Model
|
||||
{
|
||||
protected $fillable = ['item_id', 'attributes', 'options'];
|
||||
protected $casts = ['attributes' => 'array', 'options' => 'array'];
|
||||
public function item() { return $this->belongsTo(Item::class); }
|
||||
}
|
||||
|
||||
// app/Models/Items/ItemBending.php
|
||||
class ItemBending extends Model
|
||||
{
|
||||
protected $fillable = ['item_id', 'diagram', 'details'];
|
||||
protected $casts = ['details' => 'array'];
|
||||
public function item() { return $this->belongsTo(Item::class); }
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 체크리스트
|
||||
|
||||
- [ ] Item 모델 생성
|
||||
- [ ] ItemDetail 모델 생성
|
||||
- [ ] ItemAttribute 모델 생성
|
||||
- [ ] ItemBending 모델 생성
|
||||
- [ ] UnifiedItemsService 생성
|
||||
- [ ] ItemRequest (FormRequest) 생성
|
||||
- [ ] 단위 테스트 작성
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: API 엔드포인트 전환
|
||||
|
||||
### 3.1 신규 API (v2)
|
||||
|
||||
| 메서드 | 엔드포인트 | 설명 |
|
||||
|--------|-----------|------|
|
||||
| GET | /api/v2/items | 통합 품목 목록 |
|
||||
| GET | /api/v2/items/{id} | 품목 상세 (with details, attributes, bending) |
|
||||
| POST | /api/v2/items | 품목 생성 |
|
||||
| PUT | /api/v2/items/{id} | 품목 수정 |
|
||||
| DELETE | /api/v2/items/{id} | 품목 삭제 |
|
||||
| DELETE | /api/v2/items/batch | 일괄 삭제 |
|
||||
|
||||
### 3.2 체크리스트
|
||||
|
||||
- [ ] v2 라우트 정의
|
||||
- [ ] UnifiedItemsController 생성
|
||||
- [ ] Swagger 문서 작성 (v2)
|
||||
- [ ] v1 API 호환 레이어 구현
|
||||
- [ ] API 테스트 작성
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 참조 테이블 마이그레이션
|
||||
|
||||
### 4.1 참조 테이블 목록
|
||||
|
||||
| 테이블 | 변경 필요 컬럼 | 작업 |
|
||||
|--------|--------------|------|
|
||||
| product_components | ref_type+ref_id → child_item_id | 마이그레이션 |
|
||||
| bom_template_items | ref_type+ref_id → item_id | 마이그레이션 |
|
||||
| orders | product_id → item_id | 마이그레이션 |
|
||||
| order_items | product_id → item_id | 마이그레이션 |
|
||||
| quotes | product_id → item_id | 마이그레이션 |
|
||||
| material_receipts | material_id → item_id | 마이그레이션 |
|
||||
| lots | material_id → item_id | 마이그레이션 |
|
||||
| price_histories | item_type+item_id → item_id | 마이그레이션 |
|
||||
|
||||
### 4.2 체크리스트
|
||||
|
||||
- [ ] 각 참조 테이블 마이그레이션 스크립트 작성
|
||||
- [ ] 관련 모델 업데이트
|
||||
- [ ] 데이터 검증
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: 레거시 정리
|
||||
|
||||
### 5.1 체크리스트
|
||||
|
||||
- [ ] 모든 참조 테이블 전환 완료 확인
|
||||
- [ ] v1 API 사용량 모니터링 (0 확인)
|
||||
- [ ] items 레거시 컬럼 제거 (legacy_table, legacy_id)
|
||||
- [ ] item_id_mappings 테이블 제거
|
||||
- [ ] products 테이블 제거
|
||||
- [ ] materials 테이블 제거
|
||||
- [ ] 최종 테스트
|
||||
|
||||
---
|
||||
|
||||
## 테이블 구조 요약
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ items (핵심) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ id, tenant_id, item_type, code, name, unit, category_id │
|
||||
│ bom (JSON), is_active, legacy_table, legacy_id │
|
||||
│ timestamps + soft deletes │
|
||||
└───────────────────────────┬─────────────────────────────────────┘
|
||||
│ 1:1
|
||||
┌───────────────────┼───────────────────┬─────────────────┐
|
||||
▼ ▼ ▼ │
|
||||
┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
|
||||
│ item_details │ │item_attributes│ │ item_bending │ │
|
||||
├───────────────┤ ├───────────────┤ ├───────────────┤ │
|
||||
│ item_id (UK) │ │ item_id (UK) │ │ item_id (UK) │ │
|
||||
│ is_sellable │ │ attributes │ │ diagram │ │
|
||||
│ is_purchasable│ │ options │ │ details │ │
|
||||
│ is_producible │ └───────────────┘ └───────────────┘ │
|
||||
│ safety_stock │ │
|
||||
│ lead_time │ ┌───────────────────────────────────┐ │
|
||||
│ is_variable.. │ │ item_id_mappings │ │
|
||||
│ product_cat.. │ ├───────────────────────────────────┤ │
|
||||
│ part_type │ │ new_item_id ──────────────────────┼────────┘
|
||||
│ is_inspection │ │ legacy_table, legacy_id │
|
||||
└───────────────┘ └───────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 사용 패턴별 쿼리
|
||||
|
||||
| 패턴 | 빈도 | JOIN | 쿼리 |
|
||||
|------|------|------|------|
|
||||
| 목록 조회 | 80%+ | ❌ | `SELECT * FROM items WHERE ...` |
|
||||
| 검색 | 빈번 | ❌ | `SELECT * FROM items WHERE code LIKE ...` |
|
||||
| BOM 계산 | 5% | 자기참조 | `SELECT * FROM items WHERE id IN (...)` |
|
||||
| 상세 조회 | 15% | ✅ 3-4테이블 | `SELECT * FROM items JOIN item_details ...` |
|
||||
| 필터 (판매가능) | 간헐 | ✅ 1테이블 | `JOIN item_details WHERE is_sellable = 1` |
|
||||
|
||||
---
|
||||
|
||||
## 일정 계획
|
||||
|
||||
| Phase | 작업 내용 | 상태 |
|
||||
|-------|----------|------|
|
||||
| Phase 1 | 테이블 생성 + 데이터 이관 | ⬜ 대기 |
|
||||
| Phase 2 | 모델 + Service 생성 | ⬜ 대기 |
|
||||
| Phase 3 | API 엔드포인트 전환 | ⬜ 대기 |
|
||||
| Phase 4 | 참조 테이블 마이그레이션 | ⬜ 대기 |
|
||||
| Phase 5 | 레거시 정리 | ⬜ 대기 |
|
||||
|
||||
---
|
||||
|
||||
## 리스크 및 대응
|
||||
|
||||
### 리스크 1: ID 변경으로 인한 기존 연동 깨짐
|
||||
- **대응**: legacy_table + legacy_id 컬럼으로 매핑 유지
|
||||
- **전환기간**: v1 API 호환 레이어 제공
|
||||
|
||||
### 리스크 2: 데이터 이관 중 누락
|
||||
- **대응**: 이관 전후 건수 검증
|
||||
- **롤백 계획**: 이관 스크립트 역순 실행
|
||||
|
||||
### 리스크 3: JOIN 성능
|
||||
- **분석**: 1:1 JOIN + UNIQUE 인덱스로 10만건도 수 ms 이내
|
||||
- **모니터링**: 쿼리 성능 측정
|
||||
@@ -4,10 +4,36 @@
|
||||
|
||||
**작성일**: 2025-12-09
|
||||
**상태**: 계획 중
|
||||
**관련 문서**:
|
||||
- `docs/specs/item-master-field-integration.md`
|
||||
- `docs/specs/item-master-field-key-validation.md`
|
||||
- `docs/specs/ITEM-MASTER-INDEX.md`
|
||||
|
||||
---
|
||||
|
||||
## 📋 필수 참조 문서
|
||||
|
||||
개발 전 반드시 확인해야 할 문서 목록입니다.
|
||||
|
||||
### 🔴 필수 확인
|
||||
|
||||
| 문서 | 경로 | 확인 내용 |
|
||||
|------|------|----------|
|
||||
| **품목 정책** | `docs/rules/item-policy.md` | item_type 체계, 필드 키 예약어, API 규칙 |
|
||||
| **DB 스키마** | `docs/specs/database-schema.md` | item_fields, item_pages 테이블 구조 |
|
||||
| **API 개발 규칙** | `docs/standards/api-rules.md` | Service-First, FormRequest, i18n |
|
||||
|
||||
### 🟡 참고 문서
|
||||
|
||||
| 문서 | 경로 | 내용 |
|
||||
|------|------|------|
|
||||
| **품목 연동 설계** | `docs/specs/item-master-integration.md` | BE-FE 연동 아키텍처 |
|
||||
| **프론트엔드 가이드** | `docs/front/item-master-guide.md` | 페이지-섹션-필드 구조 |
|
||||
| **API 플로우 테스트** | `docs/plans/flow-tests/item-master-*.json` | ItemMaster API 테스트 시나리오 |
|
||||
|
||||
### 📁 관련 코드 파일
|
||||
|
||||
| 파일 | 경로 | 역할 |
|
||||
|------|------|------|
|
||||
| SystemFields | `api/app/Constants/SystemFields.php` | 시스템 예약어 상수 |
|
||||
| ItemTypeSeeder | `api/database/seeders/ItemTypeSeeder.php` | item_type 코드 시딩 |
|
||||
| ItemFieldService | `api/app/Services/ItemMaster/ItemFieldService.php` | 필드 CRUD 서비스 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user