feat: ItemMaster 데이터베이스 구조 구축 (9개 테이블)
- 마이그레이션 9개 생성 (unit_options, section_templates, item_master_fields, item_pages, item_sections, item_fields, item_bom_items, custom_tabs, tab_columns) - Eloquent 모델 9개 구현 (ItemMaster 네임스페이스) - ItemMasterSeeder 작성 및 테스트 데이터 생성 주요 특징: - Multi-tenant 지원 (BelongsToTenant trait) - Soft Delete 적용 (deleted_at, deleted_by) - 감사 로그 지원 (created_by, updated_by) - JSON 필드로 동적 속성 지원 (display_condition, validation_rules, options, properties) - FK 제약조건 및 Composite Index 설정 - 계층 구조 (ItemPage → ItemSection → ItemField/ItemBomItem) SAM API Development Rules 준수
This commit is contained in:
289
CURRENT_WORKS.md
289
CURRENT_WORKS.md
@@ -1,3 +1,292 @@
|
||||
## 2025-11-20 (수) - ItemMaster 데이터베이스 구조 구축
|
||||
|
||||
### 주요 작업
|
||||
- ItemMaster 시스템을 위한 9개 테이블 마이그레이션 생성 및 실행
|
||||
- 9개 Eloquent 모델 클래스 구현
|
||||
- 시드 데이터 작성 및 테스트 데이터 생성
|
||||
- SAM API Development Rules 준수 (Multi-tenant, SoftDeletes, 감사 로그)
|
||||
|
||||
### 추가된 파일
|
||||
|
||||
#### 마이그레이션 (9개)
|
||||
1. **database/migrations/2025_11_20_100000_create_unit_options_table.php**
|
||||
- 단위 옵션 테이블 (kg, EA, m, mm, L, BOX)
|
||||
- tenant_id, label, value, created_by, updated_by, deleted_by
|
||||
|
||||
2. **database/migrations/2025_11_20_100001_create_section_templates_table.php**
|
||||
- 섹션 템플릿 테이블 (재사용 가능한 섹션 정의)
|
||||
- title, type (fields/bom), description, is_default
|
||||
|
||||
3. **database/migrations/2025_11_20_100002_create_item_master_fields_table.php**
|
||||
- 마스터 필드 라이브러리
|
||||
- field_name, field_type, category, is_common
|
||||
- options, validation_rules, properties (JSON)
|
||||
|
||||
4. **database/migrations/2025_11_20_100003_create_item_pages_table.php**
|
||||
- 품목 페이지 테이블 (FG/PT/SM/RM/CS)
|
||||
- page_name, item_type (ENUM), absolute_path, is_active
|
||||
|
||||
5. **database/migrations/2025_11_20_100004_create_item_sections_table.php**
|
||||
- 섹션 인스턴스 테이블
|
||||
- page_id (FK), title, type, order_no
|
||||
- Composite Index: (page_id, order_no)
|
||||
|
||||
6. **database/migrations/2025_11_20_100005_create_item_fields_table.php**
|
||||
- 필드 인스턴스 테이블
|
||||
- section_id (FK), field_name, field_type, order_no, is_required
|
||||
- display_condition, validation_rules, options, properties (JSON)
|
||||
- Composite Index: (section_id, order_no)
|
||||
|
||||
7. **database/migrations/2025_11_20_100006_create_item_bom_items_table.php**
|
||||
- BOM 항목 테이블
|
||||
- section_id (FK), item_code, item_name, quantity, unit
|
||||
- unit_price, total_price, spec, note
|
||||
|
||||
8. **database/migrations/2025_11_20_100007_create_custom_tabs_table.php**
|
||||
- 커스텀 탭 정의
|
||||
- label, icon, is_default, order_no
|
||||
|
||||
9. **database/migrations/2025_11_20_100008_create_tab_columns_table.php**
|
||||
- 탭별 컬럼 설정
|
||||
- tab_id (FK), columns (JSON)
|
||||
- Unique: (tenant_id, tab_id)
|
||||
|
||||
#### 모델 (9개)
|
||||
1. **app/Models/ItemMaster/UnitOption.php**
|
||||
- Traits: BelongsToTenant, ModelTrait, SoftDeletes
|
||||
|
||||
2. **app/Models/ItemMaster/SectionTemplate.php**
|
||||
- is_default boolean cast
|
||||
|
||||
3. **app/Models/ItemMaster/ItemMasterField.php**
|
||||
- options, validation_rules, properties array cast
|
||||
|
||||
4. **app/Models/ItemMaster/ItemPage.php**
|
||||
- Relationship: sections() hasMany
|
||||
- is_active boolean cast
|
||||
|
||||
5. **app/Models/ItemMaster/ItemSection.php**
|
||||
- Relationships: page() belongsTo, fields() hasMany, bomItems() hasMany
|
||||
- order_no integer cast
|
||||
|
||||
6. **app/Models/ItemMaster/ItemField.php**
|
||||
- Relationship: section() belongsTo
|
||||
- 4개 JSON 필드 array cast
|
||||
|
||||
7. **app/Models/ItemMaster/ItemBomItem.php**
|
||||
- Relationship: section() belongsTo
|
||||
- quantity decimal:4, prices decimal:2
|
||||
|
||||
8. **app/Models/ItemMaster/CustomTab.php**
|
||||
- Relationship: columnSetting() hasOne
|
||||
- is_default boolean, order_no integer cast
|
||||
|
||||
9. **app/Models/ItemMaster/TabColumn.php**
|
||||
- Relationship: tab() belongsTo
|
||||
- columns array cast
|
||||
- SoftDeletes 미적용 (설정 데이터)
|
||||
|
||||
#### 시더
|
||||
1. **database/seeders/ItemMasterSeeder.php**
|
||||
- 6개 단위 옵션 생성
|
||||
- 3개 섹션 템플릿 생성
|
||||
- 8개 마스터 필드 생성
|
||||
- 5개 품목 페이지 생성 (FG, PT, SM, RM, CS)
|
||||
- 각 페이지에 기본 정보 섹션 + 필드 3개
|
||||
- FG/PT 페이지에 BOM 구성 섹션 + 샘플 BOM 항목
|
||||
- 3개 커스텀 탭 + 컬럼 설정
|
||||
|
||||
### 작업 내용
|
||||
|
||||
#### 1단계: 분석
|
||||
- 프론트엔드 API 명세서 분석
|
||||
- 기존 DB 구조와 요구사항 비교
|
||||
- 9개 테이블 간 의존성 파악
|
||||
- 마이그레이션 실행 순서 결정
|
||||
|
||||
#### 2단계: 수정
|
||||
- SAM API Development Rules 준수하여 마이그레이션 작성
|
||||
- BelongsToTenant, SoftDeletes 적용
|
||||
- created_by, updated_by, deleted_by 컬럼 (COMMENT 포함)
|
||||
- FK 제약조건 with ON DELETE CASCADE
|
||||
- Eloquent 모델 작성
|
||||
- 관계 설정 (hasMany, belongsTo, hasOne)
|
||||
- JSON 필드 캐스팅
|
||||
- ModelTrait 적용
|
||||
- 시더 작성 (실제 사용 가능한 테스트 데이터)
|
||||
|
||||
#### 3단계: 검증
|
||||
- `php artisan migrate --pretend` 실행 → 순서 확인
|
||||
- `php artisan migrate` 실행 → 9개 테이블 생성 성공
|
||||
- `php artisan db:seed --class=ItemMasterSeeder` 실행
|
||||
- 데이터 검증:
|
||||
- Unit Options: 6개
|
||||
- Item Pages: 5개
|
||||
- Item Sections: 7개
|
||||
- Item Fields: 15개
|
||||
- BOM Items: 2개
|
||||
|
||||
#### 4단계: 정리
|
||||
- 임시 파일 확인 → 없음
|
||||
|
||||
### 기술적 특징
|
||||
|
||||
#### Multi-tenant 지원
|
||||
- 모든 테이블에 tenant_id 컬럼
|
||||
- BelongsToTenant trait로 글로벌 스코프 자동 적용
|
||||
- 테넌트 격리 보장
|
||||
|
||||
#### 감사 로그 지원
|
||||
- created_by, updated_by, deleted_by 컬럼
|
||||
- 모든 변경사항 추적 가능
|
||||
|
||||
#### 유연한 스키마
|
||||
- JSON 필드로 동적 속성 지원
|
||||
- display_condition: 조건부 표시
|
||||
- validation_rules: 유효성 검사 규칙
|
||||
- options: 드롭다운 옵션
|
||||
- properties: 필드 속성 (unit, precision, format)
|
||||
|
||||
#### 정렬 및 순서 관리
|
||||
- order_no 컬럼으로 UI 순서 제어
|
||||
- Composite Index로 빠른 정렬 쿼리
|
||||
|
||||
#### 관계 설정
|
||||
- ItemPage → ItemSection → ItemField (계층 구조)
|
||||
- ItemPage → ItemSection → ItemBomItem (BOM 구조)
|
||||
- CustomTab → TabColumn (1:1 관계)
|
||||
|
||||
### 다음 단계 (Week 2-3)
|
||||
- ItemPageController, ItemSectionController 구현
|
||||
- ItemFieldController, ItemBomItemController 구현
|
||||
- FormRequest 클래스 작성
|
||||
- Service 클래스 구현 (비즈니스 로직)
|
||||
- Swagger 문서 작성 (/api/v1/item-master/*)
|
||||
|
||||
### Git 커밋
|
||||
```bash
|
||||
# 커밋 예정
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2025-11-17 (일) - is_active 컬럼 추가 (products, materials 테이블)
|
||||
|
||||
### 주요 작업
|
||||
- products 및 materials 테이블에 is_active 컬럼 추가
|
||||
- Product 및 Material 모델 업데이트
|
||||
- ModelTrait의 scopeActive() 메서드 지원
|
||||
|
||||
### 문제 상황
|
||||
- GET /api/v1/items 실행 시 `SQLSTATE[42S22]: Column not found: 1054 Unknown column 'is_active'` 에러 발생
|
||||
- ItemsService에서 `->where('is_active', 1)` 조건 사용
|
||||
- ModelTrait에 `scopeActive()` 메서드가 is_active 컬럼 참조
|
||||
- products, materials 테이블에 is_active 컬럼 없음
|
||||
|
||||
### 추가된 파일
|
||||
1. **database/migrations/2025_11_17_145307_add_is_active_to_products_and_materials_tables.php**
|
||||
- products 테이블에 is_active 추가 (tinyInteger, default 1, AFTER updated_by)
|
||||
- materials 테이블에 is_active 추가 (tinyInteger, default 1, AFTER updated_by)
|
||||
- 롤백 지원 (down 메서드)
|
||||
|
||||
### 수정된 파일
|
||||
1. **app/Models/Products/Product.php**
|
||||
- fillable에 'is_active' 추가
|
||||
- casts에 'is_active' => 'boolean' 추가
|
||||
|
||||
2. **app/Models/Materials/Material.php**
|
||||
- fillable에 'material_type', 'is_active' 추가
|
||||
- casts에 'is_active' => 'boolean' 추가
|
||||
|
||||
### 작업 내용
|
||||
- ModelTrait의 scopeActive() 메서드가 정상 작동하도록 is_active 컬럼 추가
|
||||
- 기본값 1로 설정하여 기존 데이터는 활성 상태로 유지
|
||||
- Material 모델에 누락된 material_type도 fillable에 추가
|
||||
|
||||
### Git 커밋
|
||||
```bash
|
||||
git commit 517d594
|
||||
feat: products 및 materials 테이블에 is_active 컬럼 추가
|
||||
|
||||
- is_active 컬럼 추가 마이그레이션 (default 1)
|
||||
- Product 모델 fillable 및 casts 업데이트
|
||||
- Material 모델 fillable 및 casts 업데이트
|
||||
- Material 모델에 material_type fillable 추가
|
||||
- ModelTrait의 scopeActive() 메서드 지원
|
||||
|
||||
git commit 8ce8a35
|
||||
fix: is_active 마이그레이션에 컬럼 존재 여부 체크 추가
|
||||
|
||||
- Schema::hasColumn()으로 컬럼 존재 여부 확인
|
||||
- 개발 서버와 로컬 환경의 스키마 차이 대응
|
||||
- 중복 컬럼 추가 오류 방지
|
||||
```
|
||||
|
||||
### 환경별 차이 발견
|
||||
- **개발 서버**: is_active 컬럼이 이미 존재함
|
||||
- **로컬 환경**: is_active 컬럼이 없었음
|
||||
- **원인**: 환경 간 DB 스키마 동기화가 안 되어 있었음
|
||||
- **해결**: 컬럼 존재 여부를 체크하는 로직 추가로 양쪽 환경 모두 대응
|
||||
|
||||
### 개발 서버 마이그레이션 처리 방법
|
||||
```bash
|
||||
# 방법 1: 롤백 후 재실행
|
||||
php artisan migrate:rollback --step=1
|
||||
git pull origin develop
|
||||
php artisan migrate
|
||||
|
||||
# 방법 2: 실패한 마이그레이션 레코드만 정리 (is_active가 이미 있으므로)
|
||||
# migrations 테이블에서 해당 레코드를 수동으로 완료 처리
|
||||
```
|
||||
|
||||
### 검증 완료
|
||||
- [x] 마이그레이션 실행 성공 (로컬)
|
||||
- [x] Product 모델 업데이트
|
||||
- [x] Material 모델 업데이트
|
||||
- [x] 컬럼 존재 여부 체크 추가
|
||||
- [x] Pint 포맷팅 통과
|
||||
- [x] Git 커밋 완료 (2개)
|
||||
|
||||
---
|
||||
|
||||
## 2025-11-17 (일) - BP-MES Phase 1: Swagger 문서 보완 (GET /items 엔드포인트)
|
||||
|
||||
### 주요 작업
|
||||
- ItemsApi Swagger 문서 누락 부분 추가
|
||||
- GET /api/v1/items 엔드포인트 문서화 완료
|
||||
|
||||
### 수정된 파일
|
||||
1. **app/Swagger/v1/ItemsApi.php**
|
||||
- index() 메서드 Swagger 문서 추가
|
||||
- 품목 목록 조회 (통합) 엔드포인트 문서화
|
||||
- 페이징 파라미터 (page, size) 정의
|
||||
- 검색 및 필터 파라미터 정의 (search, product_type, category_id, is_active)
|
||||
- 응답 스키마 정의 (Item array + PaginationMeta)
|
||||
|
||||
### 작업 내용
|
||||
- 라우트는 존재했지만 Swagger 문서가 누락되어 있던 GET /items 엔드포인트 문서화
|
||||
- ItemsApi.php의 5개 엔드포인트 중 index()만 문서가 없었던 문제 해결
|
||||
- l5-swagger:generate 실행으로 Swagger JSON 재생성
|
||||
|
||||
### Git 커밋
|
||||
```bash
|
||||
git commit 7b8f879
|
||||
docs: GET /items 엔드포인트 Swagger 문서 추가
|
||||
|
||||
- ItemsApi.php에 index() 메서드 문서 추가
|
||||
- 품목 목록 조회 (통합) 엔드포인트 문서화
|
||||
- 페이징, 검색, 필터 파라미터 정의
|
||||
- Swagger JSON 재생성 완료
|
||||
```
|
||||
|
||||
### 검증 완료
|
||||
- [x] Swagger 문서 추가 (index 메서드)
|
||||
- [x] Swagger JSON 재생성
|
||||
- [x] Pint 포맷팅 통과
|
||||
- [x] Git 커밋 완료
|
||||
|
||||
---
|
||||
|
||||
## 2025-11-17 (일) - BP-MES Phase 1: Items BOM API 및 File Upload API 구현 완료
|
||||
|
||||
### 주요 작업
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 논리적 데이터베이스 관계 문서
|
||||
|
||||
> **자동 생성**: 2025-11-17 13:00:34
|
||||
> **자동 생성**: 2025-11-20 16:28:56
|
||||
> **소스**: Eloquent 모델 관계 분석
|
||||
|
||||
## 📊 모델별 관계 현황
|
||||
@@ -123,6 +123,38 @@ ### folders
|
||||
**모델**: `App\Models\Folder`
|
||||
|
||||
|
||||
### custom_tabs
|
||||
**모델**: `App\Models\ItemMaster\CustomTab`
|
||||
|
||||
- **columnSetting()**: hasOne → `tab_columns`
|
||||
|
||||
### item_bom_items
|
||||
**모델**: `App\Models\ItemMaster\ItemBomItem`
|
||||
|
||||
- **section()**: belongsTo → `item_sections`
|
||||
|
||||
### item_fields
|
||||
**모델**: `App\Models\ItemMaster\ItemField`
|
||||
|
||||
- **section()**: belongsTo → `item_sections`
|
||||
|
||||
### item_pages
|
||||
**모델**: `App\Models\ItemMaster\ItemPage`
|
||||
|
||||
- **sections()**: hasMany → `item_sections`
|
||||
|
||||
### item_sections
|
||||
**모델**: `App\Models\ItemMaster\ItemSection`
|
||||
|
||||
- **page()**: belongsTo → `item_pages`
|
||||
- **fields()**: hasMany → `item_fields`
|
||||
- **bomItems()**: hasMany → `item_bom_items`
|
||||
|
||||
### tab_columns
|
||||
**모델**: `App\Models\ItemMaster\TabColumn`
|
||||
|
||||
- **tab()**: belongsTo → `custom_tabs`
|
||||
|
||||
### main_requests
|
||||
**모델**: `App\Models\MainRequest`
|
||||
|
||||
|
||||
45
app/Models/ItemMaster/CustomTab.php
Normal file
45
app/Models/ItemMaster/CustomTab.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class CustomTab extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'label',
|
||||
'icon',
|
||||
'is_default',
|
||||
'order_no',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_default' => 'boolean',
|
||||
'order_no' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 탭의 컬럼 설정
|
||||
*/
|
||||
public function columnSetting()
|
||||
{
|
||||
return $this->hasOne(TabColumn::class, 'tab_id');
|
||||
}
|
||||
}
|
||||
51
app/Models/ItemMaster/ItemBomItem.php
Normal file
51
app/Models/ItemMaster/ItemBomItem.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ItemBomItem extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'section_id',
|
||||
'item_code',
|
||||
'item_name',
|
||||
'quantity',
|
||||
'unit',
|
||||
'unit_price',
|
||||
'total_price',
|
||||
'spec',
|
||||
'note',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'quantity' => 'decimal:4',
|
||||
'unit_price' => 'decimal:2',
|
||||
'total_price' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 소속 섹션
|
||||
*/
|
||||
public function section()
|
||||
{
|
||||
return $this->belongsTo(ItemSection::class, 'section_id');
|
||||
}
|
||||
}
|
||||
56
app/Models/ItemMaster/ItemField.php
Normal file
56
app/Models/ItemMaster/ItemField.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ItemField extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'section_id',
|
||||
'field_name',
|
||||
'field_type',
|
||||
'order_no',
|
||||
'is_required',
|
||||
'default_value',
|
||||
'placeholder',
|
||||
'display_condition',
|
||||
'validation_rules',
|
||||
'options',
|
||||
'properties',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'order_no' => 'integer',
|
||||
'is_required' => 'boolean',
|
||||
'display_condition' => 'array',
|
||||
'validation_rules' => 'array',
|
||||
'options' => 'array',
|
||||
'properties' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 소속 섹션
|
||||
*/
|
||||
public function section()
|
||||
{
|
||||
return $this->belongsTo(ItemSection::class, 'section_id');
|
||||
}
|
||||
}
|
||||
44
app/Models/ItemMaster/ItemMasterField.php
Normal file
44
app/Models/ItemMaster/ItemMasterField.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ItemMasterField extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'field_name',
|
||||
'field_type',
|
||||
'category',
|
||||
'description',
|
||||
'is_common',
|
||||
'default_value',
|
||||
'options',
|
||||
'validation_rules',
|
||||
'properties',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_common' => 'boolean',
|
||||
'options' => 'array',
|
||||
'validation_rules' => 'array',
|
||||
'properties' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
}
|
||||
44
app/Models/ItemMaster/ItemPage.php
Normal file
44
app/Models/ItemMaster/ItemPage.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ItemPage extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'page_name',
|
||||
'item_type',
|
||||
'absolute_path',
|
||||
'is_active',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 페이지의 섹션 목록
|
||||
*/
|
||||
public function sections()
|
||||
{
|
||||
return $this->hasMany(ItemSection::class, 'page_id')->orderBy('order_no');
|
||||
}
|
||||
}
|
||||
60
app/Models/ItemMaster/ItemSection.php
Normal file
60
app/Models/ItemMaster/ItemSection.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ItemSection extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'page_id',
|
||||
'title',
|
||||
'type',
|
||||
'order_no',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'order_no' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 소속 페이지
|
||||
*/
|
||||
public function page()
|
||||
{
|
||||
return $this->belongsTo(ItemPage::class, 'page_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션의 필드 목록
|
||||
*/
|
||||
public function fields()
|
||||
{
|
||||
return $this->hasMany(ItemField::class, 'section_id')->orderBy('order_no');
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션의 BOM 항목 목록
|
||||
*/
|
||||
public function bomItems()
|
||||
{
|
||||
return $this->hasMany(ItemBomItem::class, 'section_id');
|
||||
}
|
||||
}
|
||||
36
app/Models/ItemMaster/SectionTemplate.php
Normal file
36
app/Models/ItemMaster/SectionTemplate.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class SectionTemplate extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'title',
|
||||
'type',
|
||||
'description',
|
||||
'is_default',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_default' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
}
|
||||
34
app/Models/ItemMaster/TabColumn.php
Normal file
34
app/Models/ItemMaster/TabColumn.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TabColumn extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'tab_id',
|
||||
'columns',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'columns' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* 소속 탭
|
||||
*/
|
||||
public function tab()
|
||||
{
|
||||
return $this->belongsTo(CustomTab::class, 'tab_id');
|
||||
}
|
||||
}
|
||||
33
app/Models/ItemMaster/UnitOption.php
Normal file
33
app/Models/ItemMaster/UnitOption.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\ItemMaster;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class UnitOption extends Model
|
||||
{
|
||||
use BelongsToTenant, ModelTrait, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'label',
|
||||
'value',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'deleted_by',
|
||||
'deleted_at',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('unit_options', function (Blueprint $table) {
|
||||
$table->id()->comment('단위 옵션 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->string('label', 100)->comment('단위 라벨');
|
||||
$table->string('value', 50)->comment('단위 값');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index('tenant_id', 'idx_unit_options_tenant_id');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_unit_options_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('unit_options');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('section_templates', function (Blueprint $table) {
|
||||
$table->id()->comment('섹션 템플릿 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->string('title')->comment('템플릿명');
|
||||
$table->enum('type', ['fields', 'bom'])->default('fields')->comment('섹션 타입 (fields: 필드형, bom: BOM형)');
|
||||
$table->text('description')->nullable()->comment('설명');
|
||||
$table->boolean('is_default')->default(false)->comment('기본 템플릿 여부');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index('tenant_id', 'idx_section_templates_tenant_id');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_section_templates_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('section_templates');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('item_master_fields', function (Blueprint $table) {
|
||||
$table->id()->comment('마스터 필드 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->string('field_name')->comment('필드명');
|
||||
$table->enum('field_type', ['textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea'])->comment('필드 타입');
|
||||
$table->string('category', 100)->nullable()->comment('카테고리');
|
||||
$table->text('description')->nullable()->comment('설명');
|
||||
$table->boolean('is_common')->default(false)->comment('공통 필드 여부');
|
||||
$table->text('default_value')->nullable()->comment('기본값');
|
||||
$table->json('options')->nullable()->comment('드롭다운 옵션 [{"label": "옵션1", "value": "val1"}]');
|
||||
$table->json('validation_rules')->nullable()->comment('검증 규칙 {"min": 0, "max": 100, "pattern": "regex"}');
|
||||
$table->json('properties')->nullable()->comment('필드 속성 {"unit": "mm", "precision": 2, "format": "YYYY-MM-DD"}');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index('tenant_id', 'idx_item_master_fields_tenant_id');
|
||||
$table->index('category', 'idx_item_master_fields_category');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_item_master_fields_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('item_master_fields');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('item_pages', function (Blueprint $table) {
|
||||
$table->id()->comment('품목 페이지 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->string('page_name')->comment('페이지명');
|
||||
$table->enum('item_type', ['FG', 'PT', 'SM', 'RM', 'CS'])->comment('품목 유형 (FG: 완제품, PT: 반제품, SM: 부자재, RM: 원자재, CS: 소모품)');
|
||||
$table->string('absolute_path', 500)->nullable()->comment('절대 경로');
|
||||
$table->boolean('is_active')->default(true)->comment('활성 여부');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index('tenant_id', 'idx_item_pages_tenant_id');
|
||||
$table->index('item_type', 'idx_item_pages_item_type');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_item_pages_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('item_pages');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('item_sections', function (Blueprint $table) {
|
||||
$table->id()->comment('섹션 인스턴스 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('page_id')->comment('페이지 ID');
|
||||
$table->string('title')->comment('섹션 제목');
|
||||
$table->enum('type', ['fields', 'bom'])->default('fields')->comment('섹션 타입 (fields: 필드형, bom: BOM형)');
|
||||
$table->integer('order_no')->default(0)->comment('정렬 순서');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'page_id'], 'idx_item_sections_tenant_page');
|
||||
$table->index(['page_id', 'order_no'], 'idx_item_sections_order');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_item_sections_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
$table->foreign('page_id', 'fk_item_sections_page')
|
||||
->references('id')->on('item_pages')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('item_sections');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('item_fields', function (Blueprint $table) {
|
||||
$table->id()->comment('필드 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('section_id')->comment('섹션 ID');
|
||||
$table->string('field_name')->comment('필드명');
|
||||
$table->enum('field_type', ['textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea'])->comment('필드 타입');
|
||||
$table->integer('order_no')->default(0)->comment('정렬 순서');
|
||||
$table->boolean('is_required')->default(false)->comment('필수 여부');
|
||||
$table->text('default_value')->nullable()->comment('기본값');
|
||||
$table->string('placeholder')->nullable()->comment('플레이스홀더');
|
||||
$table->json('display_condition')->nullable()->comment('표시 조건 {"field_id": "1", "operator": "equals", "value": "true"}');
|
||||
$table->json('validation_rules')->nullable()->comment('검증 규칙 {"min": 0, "max": 100, "pattern": "regex"}');
|
||||
$table->json('options')->nullable()->comment('드롭다운 옵션 [{"label": "옵션1", "value": "val1"}]');
|
||||
$table->json('properties')->nullable()->comment('필드 속성 {"unit": "mm", "precision": 2, "format": "YYYY-MM-DD"}');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'section_id'], 'idx_item_fields_tenant_section');
|
||||
$table->index(['section_id', 'order_no'], 'idx_item_fields_order');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_item_fields_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
$table->foreign('section_id', 'fk_item_fields_section')
|
||||
->references('id')->on('item_sections')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('item_fields');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('item_bom_items', function (Blueprint $table) {
|
||||
$table->id()->comment('BOM 항목 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('section_id')->comment('섹션 ID');
|
||||
$table->string('item_code', 100)->nullable()->comment('품목 코드');
|
||||
$table->string('item_name')->comment('품목명');
|
||||
$table->decimal('quantity', 15, 4)->default(0)->comment('수량');
|
||||
$table->string('unit', 50)->nullable()->comment('단위');
|
||||
$table->decimal('unit_price', 15, 2)->nullable()->comment('단가');
|
||||
$table->decimal('total_price', 15, 2)->nullable()->comment('총액');
|
||||
$table->text('spec')->nullable()->comment('사양');
|
||||
$table->text('note')->nullable()->comment('비고');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index(['tenant_id', 'section_id'], 'idx_item_bom_items_tenant_section');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_item_bom_items_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
$table->foreign('section_id', 'fk_item_bom_items_section')
|
||||
->references('id')->on('item_sections')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('item_bom_items');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('custom_tabs', function (Blueprint $table) {
|
||||
$table->id()->comment('커스텀 탭 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->string('label')->comment('탭 라벨');
|
||||
$table->string('icon', 100)->nullable()->comment('아이콘');
|
||||
$table->boolean('is_default')->default(false)->comment('기본 탭 여부');
|
||||
$table->integer('order_no')->default(0)->comment('정렬 순서');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->unsignedBigInteger('deleted_by')->nullable()->comment('삭제자 ID');
|
||||
$table->timestamps();
|
||||
$table->softDeletes()->comment('소프트 삭제');
|
||||
|
||||
// 인덱스
|
||||
$table->index('tenant_id', 'idx_custom_tabs_tenant_id');
|
||||
$table->index(['tenant_id', 'order_no'], 'idx_custom_tabs_order');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_custom_tabs_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('custom_tabs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tab_columns', function (Blueprint $table) {
|
||||
$table->id()->comment('탭 컬럼 설정 ID');
|
||||
$table->unsignedBigInteger('tenant_id')->comment('테넌트 ID');
|
||||
$table->unsignedBigInteger('tab_id')->comment('탭 ID');
|
||||
$table->json('columns')->comment('컬럼 설정 [{"key": "name", "label": "품목명", "visible": true, "order": 0}]');
|
||||
$table->unsignedBigInteger('created_by')->nullable()->comment('생성자 ID');
|
||||
$table->unsignedBigInteger('updated_by')->nullable()->comment('수정자 ID');
|
||||
$table->timestamps();
|
||||
|
||||
// 인덱스
|
||||
$table->unique(['tenant_id', 'tab_id'], 'uk_tab_columns_tenant_tab');
|
||||
|
||||
// 외래키
|
||||
$table->foreign('tenant_id', 'fk_tab_columns_tenant')
|
||||
->references('id')->on('tenants')
|
||||
->onDelete('cascade');
|
||||
$table->foreign('tab_id', 'fk_tab_columns_tab')
|
||||
->references('id')->on('custom_tabs')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tab_columns');
|
||||
}
|
||||
};
|
||||
221
database/seeders/ItemMasterSeeder.php
Normal file
221
database/seeders/ItemMasterSeeder.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\ItemMaster\UnitOption;
|
||||
use App\Models\ItemMaster\SectionTemplate;
|
||||
use App\Models\ItemMaster\ItemMasterField;
|
||||
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\TabColumn;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ItemMasterSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$tenantId = 1; // 기본 테넌트 ID (실제 환경에 맞게 조정)
|
||||
$userId = 1; // 기본 사용자 ID (실제 환경에 맞게 조정)
|
||||
|
||||
// 1. 단위 옵션
|
||||
$units = [
|
||||
['label' => '킬로그램', 'value' => 'kg'],
|
||||
['label' => '개', 'value' => 'EA'],
|
||||
['label' => '미터', 'value' => 'm'],
|
||||
['label' => '밀리미터', 'value' => 'mm'],
|
||||
['label' => '리터', 'value' => 'L'],
|
||||
['label' => '박스', 'value' => 'BOX'],
|
||||
];
|
||||
|
||||
foreach ($units as $unit) {
|
||||
UnitOption::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'label' => $unit['label'],
|
||||
'value' => $unit['value'],
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
// 2. 섹션 템플릿
|
||||
$templates = [
|
||||
['title' => '기본 정보', 'type' => 'fields', 'description' => '제품 기본 정보 입력용 템플릿', 'is_default' => true],
|
||||
['title' => '치수 정보', 'type' => 'fields', 'description' => '제품 치수 정보 입력용 템플릿', 'is_default' => false],
|
||||
['title' => 'BOM 구성', 'type' => 'bom', 'description' => 'BOM 항목 관리용 템플릿', 'is_default' => true],
|
||||
];
|
||||
|
||||
foreach ($templates as $template) {
|
||||
SectionTemplate::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'title' => $template['title'],
|
||||
'type' => $template['type'],
|
||||
'description' => $template['description'],
|
||||
'is_default' => $template['is_default'],
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
// 3. 마스터 필드
|
||||
$masterFields = [
|
||||
['field_name' => '제품명', 'field_type' => 'textbox', 'category' => '기본정보', 'is_common' => true],
|
||||
['field_name' => '제품코드', 'field_type' => 'textbox', 'category' => '기본정보', 'is_common' => true],
|
||||
['field_name' => '규격', 'field_type' => 'textbox', 'category' => '기본정보', 'is_common' => true],
|
||||
['field_name' => '수량', 'field_type' => 'number', 'category' => '재고정보', 'is_common' => true],
|
||||
['field_name' => '단위', 'field_type' => 'dropdown', 'category' => '재고정보', 'is_common' => true, 'options' => [
|
||||
['label' => 'kg', 'value' => 'kg'],
|
||||
['label' => 'EA', 'value' => 'EA'],
|
||||
['label' => 'm', 'value' => 'm'],
|
||||
]],
|
||||
['field_name' => '길이', 'field_type' => 'number', 'category' => '치수정보', 'is_common' => false, 'properties' => ['unit' => 'mm', 'precision' => 2]],
|
||||
['field_name' => '폭', 'field_type' => 'number', 'category' => '치수정보', 'is_common' => false, 'properties' => ['unit' => 'mm', 'precision' => 2]],
|
||||
['field_name' => '높이', 'field_type' => 'number', 'category' => '치수정보', 'is_common' => false, 'properties' => ['unit' => 'mm', 'precision' => 2]],
|
||||
];
|
||||
|
||||
foreach ($masterFields as $field) {
|
||||
ItemMasterField::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'field_name' => $field['field_name'],
|
||||
'field_type' => $field['field_type'],
|
||||
'category' => $field['category'],
|
||||
'is_common' => $field['is_common'],
|
||||
'options' => $field['options'] ?? null,
|
||||
'properties' => $field['properties'] ?? null,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
// 4. 품목 페이지 (5개 유형)
|
||||
$pages = [
|
||||
['page_name' => '완제품 관리', 'item_type' => 'FG', 'absolute_path' => '/FG/완제품 관리'],
|
||||
['page_name' => '반제품 관리', 'item_type' => 'PT', 'absolute_path' => '/PT/반제품 관리'],
|
||||
['page_name' => '부자재 관리', 'item_type' => 'SM', 'absolute_path' => '/SM/부자재 관리'],
|
||||
['page_name' => '원자재 관리', 'item_type' => 'RM', 'absolute_path' => '/RM/원자재 관리'],
|
||||
['page_name' => '소모품 관리', 'item_type' => 'CS', 'absolute_path' => '/CS/소모품 관리'],
|
||||
];
|
||||
|
||||
foreach ($pages as $index => $page) {
|
||||
$itemPage = ItemPage::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'page_name' => $page['page_name'],
|
||||
'item_type' => $page['item_type'],
|
||||
'absolute_path' => $page['absolute_path'],
|
||||
'is_active' => true,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// 각 페이지에 기본 섹션 추가
|
||||
$section1 = ItemSection::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'page_id' => $itemPage->id,
|
||||
'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,
|
||||
]);
|
||||
|
||||
ItemField::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'section_id' => $section1->id,
|
||||
'field_name' => '제품코드',
|
||||
'field_type' => 'textbox',
|
||||
'order_no' => 1,
|
||||
'is_required' => true,
|
||||
'placeholder' => '제품코드를 입력하세요',
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
ItemField::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'section_id' => $section1->id,
|
||||
'field_name' => '규격',
|
||||
'field_type' => 'textbox',
|
||||
'order_no' => 2,
|
||||
'is_required' => false,
|
||||
'placeholder' => '규격을 입력하세요',
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// BOM 섹션 (완제품, 반제품만)
|
||||
if (in_array($page['item_type'], ['FG', 'PT'])) {
|
||||
$section2 = ItemSection::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'page_id' => $itemPage->id,
|
||||
'title' => 'BOM 구성',
|
||||
'type' => 'bom',
|
||||
'order_no' => 1,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// BOM 항목 샘플
|
||||
ItemBomItem::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'section_id' => $section2->id,
|
||||
'item_code' => 'MAT-001',
|
||||
'item_name' => '철판',
|
||||
'quantity' => 10.5,
|
||||
'unit' => 'kg',
|
||||
'unit_price' => 5000.00,
|
||||
'total_price' => 52500.00,
|
||||
'spec' => '두께 2mm, 스테인리스',
|
||||
'note' => '샘플 BOM 항목',
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 커스텀 탭
|
||||
$tabs = [
|
||||
['label' => '전체', 'icon' => 'all', 'is_default' => true, 'order_no' => 0],
|
||||
['label' => '완제품', 'icon' => 'product', 'is_default' => false, 'order_no' => 1],
|
||||
['label' => '원자재', 'icon' => 'material', 'is_default' => false, 'order_no' => 2],
|
||||
];
|
||||
|
||||
foreach ($tabs as $tab) {
|
||||
$customTab = CustomTab::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'label' => $tab['label'],
|
||||
'icon' => $tab['icon'],
|
||||
'is_default' => $tab['is_default'],
|
||||
'order_no' => $tab['order_no'],
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
|
||||
// 탭별 컬럼 설정
|
||||
TabColumn::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'tab_id' => $customTab->id,
|
||||
'columns' => [
|
||||
['key' => 'name', 'label' => '품목명', 'visible' => true, 'order' => 0],
|
||||
['key' => 'code', 'label' => '품목코드', 'visible' => true, 'order' => 1],
|
||||
['key' => 'type', 'label' => '유형', 'visible' => true, 'order' => 2],
|
||||
['key' => 'price', 'label' => '가격', 'visible' => false, 'order' => 3],
|
||||
],
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
echo "✅ ItemMaster 시드 데이터 생성 완료!\n";
|
||||
echo " - 단위 옵션: ".count($units)."개\n";
|
||||
echo " - 섹션 템플릿: ".count($templates)."개\n";
|
||||
echo " - 마스터 필드: ".count($masterFields)."개\n";
|
||||
echo " - 품목 페이지: ".count($pages)."개\n";
|
||||
echo " - 커스텀 탭: ".count($tabs)."개\n";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user