diff --git a/app/Http/Controllers/Api/Admin/ItemManagementApiController.php b/app/Http/Controllers/Api/Admin/ItemManagementApiController.php index d88e2199..9f38ba07 100644 --- a/app/Http/Controllers/Api/Admin/ItemManagementApiController.php +++ b/app/Http/Controllers/Api/Admin/ItemManagementApiController.php @@ -74,6 +74,18 @@ public function history(int $id): JsonResponse return response()->json($history); } + /** + * 절곡BOM 트리 (JSON - 중앙 패널) + * FG 품목 선택 시 해당 FG의 절곡 관련 BOM만, 미선택 시 전체 절곡 품목 + */ + public function bendingBomTree(Request $request): JsonResponse + { + $itemId = $request->input('item_id') ? (int) $request->input('item_id') : null; + $tree = $this->service->getBendingBomTree($itemId); + + return response()->json($tree); + } + /** * 수식 기반 BOM 산출 (API 서버의 FormulaEvaluatorService HTTP 호출) */ diff --git a/app/Services/ItemManagementService.php b/app/Services/ItemManagementService.php index fd9c0089..4142c9da 100644 --- a/app/Services/ItemManagementService.php +++ b/app/Services/ItemManagementService.php @@ -229,6 +229,127 @@ private function getActionLabel(string $action): string }; } + /** + * 절곡BOM 트리 조회 (SF-BND 품목 기반) + * FG 품목 선택 시: 해당 FG의 BOM에서 절곡 관련 품목만 필터 + * 그 외: 전체 절곡 품목(SF-BND) 트리 반환 + */ + public function getBendingBomTree(?int $itemId = null): array + { + $tenantId = session('selected_tenant_id'); + + // FG 품목이 선택된 경우: 해당 FG의 BOM에서 절곡 관련만 추출 + if ($itemId) { + $item = Item::withoutGlobalScopes() + ->where('tenant_id', $tenantId) + ->find($itemId); + + if ($item && $item->item_type === 'FG') { + return $this->buildFgBendingTree($item, $tenantId); + } + } + + // 전체 절곡 품목 트리 + $bendingItems = Item::withoutGlobalScopes() + ->where('tenant_id', $tenantId) + ->where('code', 'like', 'SF-BND%') + ->active() + ->orderBy('code') + ->get(); + + $nodes = []; + foreach ($bendingItems as $bendingItem) { + $nodes[] = $this->buildBomNode($bendingItem, 0, 3, []); + } + + return $nodes; + } + + /** + * FG 품목의 BOM에서 절곡 관련 품목만 추출하여 트리 구성 + */ + private function buildFgBendingTree(Item $fgItem, int $tenantId): array + { + $bomData = $fgItem->bom ?? []; + if (empty($bomData)) { + return []; + } + + $childIds = array_column($bomData, 'child_item_id'); + $children = Item::withoutGlobalScopes() + ->where('tenant_id', $tenantId) + ->whereIn('id', $childIds) + ->get() + ->keyBy('id'); + + $nodes = []; + foreach ($bomData as $bom) { + $child = $children->get($bom['child_item_id']); + if (! $child) { + continue; + } + + // 절곡 관련 품목이면 바로 추가 + if (str_starts_with($child->code, 'SF-BND')) { + $node = $this->buildBomNode($child, 0, 3, []); + $node['quantity'] = $bom['quantity'] ?? 1; + $nodes[] = $node; + + continue; + } + + // SF 품목이면 그 하위에서 절곡 품목 탐색 + if ($child->item_type === 'SF' || $child->item_type === 'PT') { + $subBending = $this->findBendingChildren($child, $tenantId, 1, []); + $nodes = array_merge($nodes, $subBending); + } + } + + return $nodes; + } + + /** + * 재귀적으로 절곡 관련 자식 품목 탐색 + */ + private function findBendingChildren(Item $item, int $tenantId, int $depth, array $visited): array + { + if (in_array($item->id, $visited) || $depth >= 5) { + return []; + } + $visited[] = $item->id; + + $bomData = $item->bom ?? []; + if (empty($bomData)) { + return []; + } + + $childIds = array_column($bomData, 'child_item_id'); + $children = Item::withoutGlobalScopes() + ->where('tenant_id', $tenantId) + ->whereIn('id', $childIds) + ->get() + ->keyBy('id'); + + $nodes = []; + foreach ($bomData as $bom) { + $child = $children->get($bom['child_item_id']); + if (! $child) { + continue; + } + + if (str_starts_with($child->code, 'SF-BND')) { + $node = $this->buildBomNode($child, 0, 3, []); + $node['quantity'] = $bom['quantity'] ?? 1; + $nodes[] = $node; + } elseif ($child->item_type === 'SF' || $child->item_type === 'PT') { + $subNodes = $this->findBendingChildren($child, $tenantId, $depth + 1, $visited); + $nodes = array_merge($nodes, $subNodes); + } + } + + return $nodes; + } + // ── Private ── private function buildBomNode(Item $item, int $depth, int $maxDepth, array $visited): array diff --git a/resources/views/item-management/index.blade.php b/resources/views/item-management/index.blade.php index 854fb8a3..a4dde5e7 100644 --- a/resources/views/item-management/index.blade.php +++ b/resources/views/item-management/index.blade.php @@ -50,6 +50,11 @@ class="bom-tab px-3 py-1.5 text-xs font-medium rounded-md bg-blue-100 text-blue- onclick="switchBomTab('static')"> 정적 BOM +