diff --git a/plans/mng-item-field-management-plan.md b/plans/mng-item-field-management-plan.md new file mode 100644 index 0000000..b769486 --- /dev/null +++ b/plans/mng-item-field-management-plan.md @@ -0,0 +1,531 @@ +# MNG 품목기준 필드 관리 개발 계획 + +> 테넌트별 품목기준관리(ItemMaster) 시스템 필드 시딩 및 커스텀 필드 관리 기능 + +**작성일**: 2025-12-09 +**상태**: 계획 중 +**관련 문서**: +- `docs/specs/item-master-field-integration.md` +- `docs/specs/item-master-field-key-validation.md` +- `docs/specs/ITEM-MASTER-INDEX.md` + +--- + +## 1. 개요 + +### 1.1 목적 +- 테넌트별 품목기준 **시스템 필드(고정 컬럼)** 일괄 등록/삭제 +- 신규 테넌트 생성 시 초기 필드 데이터 자동 시딩 +- 기존 테넌트에 필드 데이터 수동 시딩 +- **커스텀 필드** 추가/삭제 관리 +- 향후 회계, 생산 등 다양한 도메인 확장 지원 + +### 1.2 핵심 개념 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 품목기준 필드 구분 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ 시스템 필드 (System Fields) │ │ +│ │ ───────────────────────────────── │ │ +│ │ - products 테이블 고정 컬럼 │ │ +│ │ (code, name, unit, is_active...) │ │ +│ │ - materials 테이블 고정 컬럼 │ │ +│ │ (material_code, name, spec...) │ │ +│ │ - storage_type = 'column' │ │ +│ │ - 시딩으로 일괄 등록 │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────┐ │ +│ │ 커스텀 필드 (Custom Fields) │ │ +│ │ ───────────────────────────────── │ │ +│ │ - 테넌트별 추가 필드 │ │ +│ │ - attributes JSON에 저장 │ │ +│ │ - storage_type = 'json' │ │ +│ │ - MNG에서 수동 추가/삭제 │ │ +│ └─────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 대상 테이블 (source_table) + +| source_table | 설명 | item_type | +|--------------|------|-----------| +| `products` | 제품 테이블 | FG (완제품), PT (부품) | +| `materials` | 자재 테이블 | SM (부자재), RM (원자재), CS (소모품) | +| `product_components` | BOM 테이블 | - | +| `material_inspections` | 자재 검수 | - | +| `material_receipts` | 자재 입고 | - | + +--- + +## 2. 기능 설계 + +### 2.1 메인 화면: 품목기준 필드 관리 + +**URL**: `GET /item-master/fields` + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 품목기준 필드 관리 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ 테넌트: [현재 테넌트명] │ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ 시스템 필드 시딩 │ │ +│ │ ───────────────────────────────────────────────────────│ │ +│ │ │ │ +│ │ 소스 테이블별 상태: │ │ +│ │ ┌────────────────┬──────────┬─────────┬────────────┐ │ │ +│ │ │ 테이블 │ 필드 수 │ 상태 │ 액션 │ │ │ +│ │ ├────────────────┼──────────┼─────────┼────────────┤ │ │ +│ │ │ products │ 12/12 │ ●완료 │ [초기화] │ │ │ +│ │ │ materials │ 0/8 │ ○미등록 │ [시딩] │ │ │ +│ │ │ product_comp.. │ 5/5 │ ●완료 │ [초기화] │ │ │ +│ │ │ material_ins.. │ 0/8 │ ○미등록 │ [시딩] │ │ │ +│ │ │ material_rec.. │ 0/10 │ ○미등록 │ [시딩] │ │ │ +│ │ └────────────────┴──────────┴─────────┴────────────┘ │ │ +│ │ │ │ +│ │ [전체 시딩] [전체 초기화] │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ 커스텀 필드 관리 [+ 추가] │ │ +│ │ ───────────────────────────────────────────────────────│ │ +│ │ │ │ +│ │ 필터: [소스 테이블 ▼] [필드 타입 ▼] │ │ +│ │ │ │ +│ │ ┌───┬─────────┬───────────┬────────┬────────┬───────┐│ │ +│ │ │ □ │ 필드키 │ 필드명 │ 타입 │ 소스 │ 액션 ││ │ +│ │ ├───┼─────────┼───────────┼────────┼────────┼───────┤│ │ +│ │ │ □ │ weight │ 무게 │ number │products│ [삭제]││ │ +│ │ │ □ │ grade │ 등급 │dropdown│materials│[삭제]││ │ +│ │ │ □ │ color │ 색상 │ textbox│products│ [삭제]││ │ +│ │ └───┴─────────┴───────────┴────────┴────────┴───────┘│ │ +│ │ │ │ +│ │ [선택 삭제] │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 2.2 시스템 필드 시딩 + +**기능**: 특정 source_table의 시스템 필드를 item_fields에 일괄 등록 + +**시딩 데이터 예시** (products): +```php +[ + ['field_key' => 'code', 'field_name' => '품목코드', 'field_type' => 'textbox', 'is_required' => true], + ['field_key' => 'name', 'field_name' => '품목명', 'field_type' => 'textbox', 'is_required' => true], + ['field_key' => 'unit', 'field_name' => '단위', 'field_type' => 'dropdown', 'is_required' => true], + ['field_key' => 'product_type', 'field_name' => '제품유형', 'field_type' => 'dropdown'], + ['field_key' => 'is_sellable', 'field_name' => '판매가능', 'field_type' => 'checkbox'], + ['field_key' => 'is_purchasable', 'field_name' => '구매가능', 'field_type' => 'checkbox'], + ['field_key' => 'is_producible', 'field_name' => '생산가능', 'field_type' => 'checkbox'], + ['field_key' => 'is_active', 'field_name' => '활성화', 'field_type' => 'checkbox'], + // ... +] +``` + +**저장 시 설정**: +```php +[ + 'tenant_id' => $currentTenantId, + 'source_table' => 'products', + 'source_column' => 'code', // field_key와 동일 + 'storage_type' => 'column', // DB 컬럼 직접 저장 + 'is_system' => true, // 시스템 필드 표시 (선택적) +] +``` + +### 2.3 시스템 필드 초기화 + +**기능**: 특정 source_table의 시스템 필드를 삭제하고 다시 시딩 + +**주의사항**: +- 커스텀 필드는 유지 (storage_type = 'json'인 필드) +- 확인 다이얼로그 필수 +- 삭제 전 관련 entity_relationships도 정리 필요 + +### 2.4 커스텀 필드 추가 + +**URL**: `POST /item-master/fields/custom` + +**추가 모달**: +``` +┌─────────────────────────────────────────────┐ +│ 커스텀 필드 추가 [닫기] │ +├─────────────────────────────────────────────┤ +│ │ +│ 소스 테이블: [products ▼] │ +│ │ +│ 필드 키: [_______________] │ +│ * 영문, 숫자, 언더스코어만 허용 │ +│ * 시스템 예약어 사용 불가 │ +│ │ +│ 필드명: [_______________] │ +│ │ +│ 필드 타입: [textbox ▼] │ +│ - textbox, number, dropdown, │ +│ checkbox, date, textarea │ +│ │ +│ 필수 여부: [ ] 필수 │ +│ │ +│ 기본값: [_______________] (선택) │ +│ │ +│ 옵션 (dropdown 선택 시): │ +│ ┌─────────────────────────────────────┐ │ +│ │ 라벨 │ 값 │ │ │ +│ │ [옵션1 ] │ [val1 ] │ [+ 추가] │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ [취소] [저장] │ +│ │ +└─────────────────────────────────────────────┘ +``` + +**저장 시 설정**: +```php +[ + 'tenant_id' => $currentTenantId, + 'source_table' => 'products', + 'source_column' => null, // 커스텀 필드는 null + 'storage_type' => 'json', // JSON 저장 + 'json_path' => 'attributes.custom_weight', // 저장 경로 + 'is_system' => false, +] +``` + +### 2.5 커스텀 필드 삭제 + +**기능**: 선택한 커스텀 필드 삭제 + +**주의사항**: +- 시스템 필드(storage_type = 'column')는 삭제 불가 +- 이미 데이터가 있는 경우 경고 +- entity_relationships 정리 필요 + +--- + +## 3. 데이터 구조 + +### 3.1 item_fields 테이블 (기존 + 확장) + +```sql +item_fields +├── id +├── tenant_id +├── field_key -- 고유 키 (code, name, custom_weight) +├── field_name -- 표시명 (품목코드, 품목명, 커스텀 무게) +├── field_type -- 필드 타입 (textbox, dropdown...) +├── is_required -- 필수 여부 +├── order_no -- 정렬 순서 +├── default_value -- 기본값 +├── options -- 드롭다운 옵션 JSON +├── properties -- 속성 JSON +├── validation_rules -- 검증 규칙 JSON +├── source_table -- 소스 테이블 (products, materials) +├── source_column -- 소스 컬럼 (시스템 필드만) +├── storage_type -- 저장 방식 (column, json) +├── json_path -- JSON 저장 경로 (커스텀 필드만) +├── is_active +├── created_at / updated_at / deleted_at +``` + +### 3.2 시스템 필드 정의 (SystemFields 상수) + +```php +// app/Constants/SystemFields.php 활용 +// 이미 구현된 상수 클래스에서 시딩 데이터 추출 + +class SystemFields +{ + public const PRODUCTS = [ + 'code', 'name', 'unit', 'product_type', 'category_id', + 'is_sellable', 'is_purchasable', 'is_producible', 'is_active', + 'certification_number', 'certification_date', 'certification_expiry', + // ... + ]; + + public const MATERIALS = [ + 'material_code', 'name', 'item_name', 'specification', + 'unit', 'category_id', 'is_inspection', 'search_tag', + // ... + ]; +} +``` + +--- + +## 4. 파일 구조 + +``` +mng/ +├── app/ +│ ├── Http/ +│ │ └── Controllers/ +│ │ └── ItemFieldController.php # 신규 +│ ├── Models/ +│ │ └── ItemField.php # 신규 (MNG 전용) +│ ├── Services/ +│ │ └── ItemFieldSeedingService.php # 신규: 시딩 로직 +│ └── Constants/ +│ └── SystemFieldDefinitions.php # 신규: 시딩 데이터 정의 +├── resources/ +│ └── views/ +│ └── item-fields/ +│ ├── index.blade.php # 메인 화면 +│ ├── _seeding-section.blade.php # 시딩 섹션 (partial) +│ ├── _custom-fields-section.blade.php # 커스텀 필드 섹션 +│ └── _create-modal.blade.php # 추가 모달 +└── routes/ + └── web.php # 라우트 추가 +``` + +--- + +## 5. 구현 단계 + +### Phase 1: 기반 구조 (0.5일) + +| 작업 | 파일 | 설명 | +|------|------|------| +| ItemField 모델 | `app/Models/ItemField.php` | API DB 참조 모델 | +| 상수 클래스 | `app/Constants/SystemFieldDefinitions.php` | 테이블별 시딩 데이터 | +| 라우트 등록 | `routes/web.php` | CRUD 라우트 | +| 메뉴 추가 | 사이드바 | 품목기준 > 필드 관리 | + +### Phase 2: 시딩 기능 (1일) + +| 작업 | 파일 | 설명 | +|------|------|------| +| 시딩 서비스 | `ItemFieldSeedingService.php` | 시딩/초기화 로직 | +| 메인 화면 | `index.blade.php` | 시딩 상태 표시 | +| 시딩 API | `ItemFieldController.php` | 시딩/초기화 엔드포인트 | +| 확인 다이얼로그 | JS | 초기화 확인 | + +### Phase 3: 커스텀 필드 관리 (1일) + +| 작업 | 파일 | 설명 | +|------|------|------| +| 커스텀 필드 목록 | `index.blade.php` | 목록 표시 | +| 추가 모달 | `_create-modal.blade.php` | 필드 추가 UI | +| 필드 키 검증 | `ItemFieldController.php` | 예약어/중복 체크 | +| 삭제 기능 | `ItemFieldController.php` | 삭제 처리 | + +### Phase 4: 테스트/마무리 (0.5일) + +| 작업 | 설명 | +|------|------| +| 시딩 테스트 | 전체/개별 시딩 | +| 초기화 테스트 | 초기화 후 재시딩 | +| 커스텀 필드 테스트 | 추가/삭제 | +| 예약어 검증 테스트 | 시스템 필드명 입력 시도 | + +--- + +## 6. API 엔드포인트 + +| 메서드 | URL | 설명 | +|--------|-----|------| +| GET | `/item-master/fields` | 필드 관리 메인 화면 | +| GET | `/item-master/fields/status` | 테이블별 시딩 상태 조회 (AJAX) | +| POST | `/item-master/fields/seed` | 시스템 필드 시딩 | +| POST | `/item-master/fields/reset` | 시스템 필드 초기화 | +| GET | `/item-master/fields/custom` | 커스텀 필드 목록 (AJAX) | +| POST | `/item-master/fields/custom` | 커스텀 필드 추가 | +| DELETE | `/item-master/fields/custom/{id}` | 커스텀 필드 삭제 | + +--- + +## 7. 체크리스트 + +### Phase 1: 기반 구조 +- [ ] ItemField 모델 생성 (API DB 참조) +- [ ] SystemFieldDefinitions 상수 클래스 생성 +- [ ] 라우트 등록 +- [ ] 사이드바 메뉴 추가 + +### Phase 2: 시딩 기능 +- [ ] ItemFieldSeedingService 생성 +- [ ] 테이블별 시딩 상태 조회 +- [ ] 단일 테이블 시딩 +- [ ] 전체 테이블 시딩 +- [ ] 시스템 필드 초기화 +- [ ] 확인 다이얼로그 + +### Phase 3: 커스텀 필드 +- [ ] 커스텀 필드 목록 조회 +- [ ] 커스텀 필드 추가 모달 +- [ ] field_key 예약어 검증 연동 +- [ ] 커스텀 필드 삭제 +- [ ] 일괄 삭제 + +### Phase 4: 마무리 +- [ ] 시딩 테스트 +- [ ] 초기화 테스트 +- [ ] 커스텀 필드 테스트 +- [ ] 에러 처리 + +--- + +## 8. SystemFieldDefinitions 상수 클래스 + +```php + 'code', 'field_name' => '품목코드', 'field_type' => 'textbox', 'is_required' => true, 'order_no' => 1], + ['field_key' => 'name', 'field_name' => '품목명', 'field_type' => 'textbox', 'is_required' => true, 'order_no' => 2], + ['field_key' => 'unit', 'field_name' => '단위', 'field_type' => 'dropdown', 'is_required' => true, 'order_no' => 3], + ['field_key' => 'product_type', 'field_name' => '제품유형', 'field_type' => 'dropdown', 'order_no' => 4, 'options' => [ + ['label' => '완제품', 'value' => 'FG'], + ['label' => '부품', 'value' => 'PT'], + ]], + ['field_key' => 'category_id', 'field_name' => '카테고리', 'field_type' => 'dropdown', 'order_no' => 5], + ['field_key' => 'is_sellable', 'field_name' => '판매가능', 'field_type' => 'checkbox', 'order_no' => 6, 'default_value' => 'true'], + ['field_key' => 'is_purchasable', 'field_name' => '구매가능', 'field_type' => 'checkbox', 'order_no' => 7], + ['field_key' => 'is_producible', 'field_name' => '생산가능', 'field_type' => 'checkbox', 'order_no' => 8, 'default_value' => 'true'], + ['field_key' => 'is_active', 'field_name' => '활성화', 'field_type' => 'checkbox', 'order_no' => 9, 'default_value' => 'true'], + ['field_key' => 'certification_number', 'field_name' => '인증번호', 'field_type' => 'textbox', 'order_no' => 10], + ['field_key' => 'certification_date', 'field_name' => '인증일자', 'field_type' => 'date', 'order_no' => 11], + ['field_key' => 'certification_expiry', 'field_name' => '인증만료일', 'field_type' => 'date', 'order_no' => 12], + ]; + + /** + * materials 테이블 시스템 필드 정의 + */ + public const MATERIALS = [ + ['field_key' => 'material_code', 'field_name' => '자재코드', 'field_type' => 'textbox', 'is_required' => true, 'order_no' => 1], + ['field_key' => 'name', 'field_name' => '자재명', 'field_type' => 'textbox', 'is_required' => true, 'order_no' => 2], + ['field_key' => 'item_name', 'field_name' => '품목명', 'field_type' => 'textbox', 'order_no' => 3], + ['field_key' => 'specification', 'field_name' => '규격', 'field_type' => 'textbox', 'order_no' => 4], + ['field_key' => 'unit', 'field_name' => '단위', 'field_type' => 'dropdown', 'is_required' => true, 'order_no' => 5], + ['field_key' => 'category_id', 'field_name' => '카테고리', 'field_type' => 'dropdown', 'order_no' => 6], + ['field_key' => 'is_inspection', 'field_name' => '검수필요', 'field_type' => 'checkbox', 'order_no' => 7], + ['field_key' => 'search_tag', 'field_name' => '검색태그', 'field_type' => 'textarea', 'order_no' => 8], + ]; + + /** + * product_components 테이블 (BOM) 시스템 필드 정의 + */ + public const PRODUCT_COMPONENTS = [ + ['field_key' => 'ref_type', 'field_name' => '참조유형', 'field_type' => 'dropdown', 'order_no' => 1, 'options' => [ + ['label' => '제품', 'value' => 'product'], + ['label' => '자재', 'value' => 'material'], + ]], + ['field_key' => 'ref_id', 'field_name' => '참조품목', 'field_type' => 'dropdown', 'order_no' => 2], + ['field_key' => 'quantity', 'field_name' => '수량', 'field_type' => 'number', 'is_required' => true, 'order_no' => 3], + ['field_key' => 'formula', 'field_name' => '계산공식', 'field_type' => 'textbox', 'order_no' => 4], + ['field_key' => 'note', 'field_name' => '비고', 'field_type' => 'textarea', 'order_no' => 5], + ]; + + /** + * material_inspections 테이블 시스템 필드 정의 + */ + public const MATERIAL_INSPECTIONS = [ + ['field_key' => 'inspection_date', 'field_name' => '검수일', 'field_type' => 'date', 'is_required' => true, 'order_no' => 1], + ['field_key' => 'inspector_id', 'field_name' => '검수자', 'field_type' => 'dropdown', 'order_no' => 2], + ['field_key' => 'status', 'field_name' => '검수상태', 'field_type' => 'dropdown', 'order_no' => 3, 'options' => [ + ['label' => '대기', 'value' => 'pending'], + ['label' => '진행중', 'value' => 'in_progress'], + ['label' => '완료', 'value' => 'completed'], + ['label' => '불합격', 'value' => 'rejected'], + ]], + ['field_key' => 'lot_no', 'field_name' => 'LOT번호', 'field_type' => 'textbox', 'order_no' => 4], + ['field_key' => 'quantity', 'field_name' => '검수수량', 'field_type' => 'number', 'order_no' => 5], + ['field_key' => 'passed_quantity', 'field_name' => '합격수량', 'field_type' => 'number', 'order_no' => 6], + ['field_key' => 'rejected_quantity', 'field_name' => '불합격수량', 'field_type' => 'number', 'order_no' => 7], + ['field_key' => 'note', 'field_name' => '비고', 'field_type' => 'textarea', 'order_no' => 8], + ]; + + /** + * material_receipts 테이블 시스템 필드 정의 + */ + public const MATERIAL_RECEIPTS = [ + ['field_key' => 'receipt_date', 'field_name' => '입고일', 'field_type' => 'date', 'is_required' => true, 'order_no' => 1], + ['field_key' => 'lot_no', 'field_name' => 'LOT번호', 'field_type' => 'textbox', 'order_no' => 2], + ['field_key' => 'quantity', 'field_name' => '입고수량', 'field_type' => 'number', 'is_required' => true, 'order_no' => 3], + ['field_key' => 'unit_price', 'field_name' => '단가', 'field_type' => 'number', 'order_no' => 4], + ['field_key' => 'total_price', 'field_name' => '금액', 'field_type' => 'number', 'order_no' => 5], + ['field_key' => 'supplier_id', 'field_name' => '공급업체', 'field_type' => 'dropdown', 'order_no' => 6], + ['field_key' => 'warehouse_id', 'field_name' => '입고창고', 'field_type' => 'dropdown', 'order_no' => 7], + ['field_key' => 'po_number', 'field_name' => '발주번호', 'field_type' => 'textbox', 'order_no' => 8], + ['field_key' => 'invoice_number', 'field_name' => '송장번호', 'field_type' => 'textbox', 'order_no' => 9], + ['field_key' => 'note', 'field_name' => '비고', 'field_type' => 'textarea', 'order_no' => 10], + ]; + + /** + * 소스 테이블 목록 + */ + public const SOURCE_TABLES = [ + 'products' => '제품', + 'materials' => '자재', + 'product_components' => 'BOM', + 'material_inspections' => '자재검수', + 'material_receipts' => '자재입고', + ]; + + /** + * 소스 테이블별 필드 정의 가져오기 + */ + public static function getFieldsFor(string $sourceTable): array + { + return match ($sourceTable) { + 'products' => self::PRODUCTS, + 'materials' => self::MATERIALS, + 'product_components' => self::PRODUCT_COMPONENTS, + 'material_inspections' => self::MATERIAL_INSPECTIONS, + 'material_receipts' => self::MATERIAL_RECEIPTS, + default => [], + }; + } + + /** + * 전체 테이블 필드 수 조회 + */ + public static function getTotalFieldCount(string $sourceTable): int + { + return count(self::getFieldsFor($sourceTable)); + } +} +``` + +--- + +## 9. 향후 확장 + +### 9.1 신규 도메인 추가 시 +1. `SystemFieldDefinitions`에 상수 추가 +2. `SOURCE_TABLES`에 테이블 추가 +3. MNG에서 해당 테이블 시딩 + +### 9.2 예정 도메인 +- [ ] 회계 (journals, accounts) +- [ ] 생산 (work_orders, production_records) +- [ ] 재고 (inventories, stock_movements) +- [ ] 품질 (quality_controls) + +--- + +## 변경 이력 + +| 날짜 | 내용 | +|------|------| +| 2025-12-09 | 문서 목적 수정: 권한 관리 → 품목기준 필드 시딩/관리 | +| 2025-12-09 | 초안 작성 | \ No newline at end of file