'array', 'attributes' => 'array', 'attributes_archive' => 'array', 'options' => 'array', 'is_active' => 'boolean', ]; protected $hidden = [ 'deleted_at', ]; /** * item_type 상수 */ public const TYPE_FINISHED_GOODS = 'FG'; // 완제품 public const TYPE_PARTS = 'PT'; // 부품 public const TYPE_SUB_MATERIALS = 'SM'; // 부자재 public const TYPE_RAW_MATERIALS = 'RM'; // 원자재 public const TYPE_CONSUMABLES = 'CS'; // 소모품 /** * Products 타입 (FG, PT) */ public const PRODUCT_TYPES = ['FG', 'PT']; /** * Materials 타입 (SM, RM, CS) */ public const MATERIAL_TYPES = ['SM', 'RM', 'CS']; // ────────────────────────────────────────────────────────────── // 관계 // ────────────────────────────────────────────────────────────── /** * 상세 정보 (1:1) */ public function details() { return $this->hasOne(ItemDetail::class); } /** * 재고 정보 (1:1) */ public function stock() { return $this->hasOne(\App\Models\Tenants\Stock::class); } /** * 카테고리 */ public function category() { return $this->belongsTo(Category::class, 'category_id'); } /** * BOM 자식 품목들 (JSON bom 필드 기반) * 주의: 이 관계는 bom JSON에서 child_item_id를 추출하여 조회 */ public function bomChildren() { $childIds = collect($this->bom ?? [])->pluck('child_item_id')->filter()->toArray(); return self::whereIn('id', $childIds); } /** * 파일 (document_id/document_type 기반) * * ItemsFileController가 document_id=item_id, document_type='1'(group_id)로 저장 * morphMany(fileable) 대신 hasMany + where 조건 사용 */ public function files() { return $this->hasMany(File::class, 'document_id') ->where('document_type', '1'); // ITEM_GROUP_ID } /** * 태그 (폴리모픽) */ public function tags() { return $this->morphToMany(Tag::class, 'taggable'); } // ────────────────────────────────────────────────────────────── // 스코프 // ────────────────────────────────────────────────────────────── /** * 특정 타입 필터 * join 시 ambiguous 에러 방지를 위해 테이블 prefix 사용 */ public function scopeType($query, string $type) { return $query->where('items.item_type', strtoupper($type)); } /** * Products 타입만 (FG, PT) * join 시 ambiguous 에러 방지를 위해 테이블 prefix 사용 */ public function scopeProducts($query) { return $query->whereIn('items.item_type', self::PRODUCT_TYPES); } /** * Materials 타입만 (SM, RM, CS) * join 시 ambiguous 에러 방지를 위해 테이블 prefix 사용 */ public function scopeMaterials($query) { return $query->whereIn('items.item_type', self::MATERIAL_TYPES); } /** * 활성 품목만 */ public function scopeActive($query) { return $query->where('is_active', true); } // ────────────────────────────────────────────────────────────── // 헬퍼 메서드 // ────────────────────────────────────────────────────────────── /** * Product 타입인지 확인 */ public function isProduct(): bool { return in_array($this->item_type, self::PRODUCT_TYPES); } /** * Material 타입인지 확인 */ public function isMaterial(): bool { return in_array($this->item_type, self::MATERIAL_TYPES); } /** * BOM 자식 품목 ID 목록 추출 */ public function getBomChildIds(): array { return collect($this->bom ?? [])->pluck('child_item_id')->filter()->toArray(); } /** * BOM 자식 품목들 조회 및 모델에 설정 */ public function loadBomChildren(): self { $childIds = $this->getBomChildIds(); if (empty($childIds)) { $this->setRelation('bom_children', collect()); return $this; } $children = self::whereIn('id', $childIds)->get()->keyBy('id'); $this->setRelation('bom_children', $children); return $this; } }