From 16b8dbcc6fdc1a9d32904969f1da654830847342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Sun, 22 Feb 2026 03:50:18 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=A0=88=EA=B3=A1=20=EC=9E=90=EC=9E=AC?= =?UTF-8?q?=ED=88=AC=EC=9E=85=20dynamic=5Fbom=20=EC=88=98=EB=9F=89=20?= =?UTF-8?q?=EB=B3=B4=EC=A0=95=20=EB=B0=8F=20=EA=B0=9C=EC=86=8C=EB=8B=B9=20?= =?UTF-8?q?=EC=88=98=EB=9F=89=20=EC=82=B0=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getMaterialsForItem(): dynamic_bom 우선 체크 추가 (정적 BOM만 확인하던 문제) - dynamic_bom.qty를 woItem.quantity로 나눠 개소당 수량 산출 (작업일지 bendingInfo와 일치) - getMaterials(): 동일하게 개소당 수량으로 변환 - 응답에 lot_prefix, part_type, category 필드 추가 Co-Authored-By: Claude Opus 4.6 --- app/Services/WorkOrderService.php | 56 ++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/app/Services/WorkOrderService.php b/app/Services/WorkOrderService.php index 3d925ce..3311d68 100644 --- a/app/Services/WorkOrderService.php +++ b/app/Services/WorkOrderService.php @@ -1229,16 +1229,18 @@ public function getMaterials(int $workOrderId): array // 합산 키: (item_id, work_order_item_id) 쌍 $key = $childItemId.'_'.$woItem->id; + // dynamic_bom.qty는 아이템 수량이 곱해져 있으므로 나눠서 개소당 수량 산출 $bomQty = (float) ($bomEntry['qty'] ?? 1); - $requiredQty = $bomQty * ($woItem->quantity ?? 1); + $woItemQty = max(1, (float) ($woItem->quantity ?? 1)); + $perNodeQty = $bomQty / $woItemQty; if (isset($uniqueMaterials[$key])) { - $uniqueMaterials[$key]['required_qty'] += $requiredQty; + $uniqueMaterials[$key]['required_qty'] += $perNodeQty; } else { $uniqueMaterials[$key] = [ 'item' => $dynamicItems[$childItemId], - 'bom_qty' => $bomQty, - 'required_qty' => $requiredQty, + 'bom_qty' => $perNodeQty, + 'required_qty' => $perNodeQty, 'work_order_item_id' => $woItem->id, 'lot_prefix' => $bomEntry['lot_prefix'] ?? null, 'part_type' => $bomEntry['part_type'] ?? null, @@ -2847,7 +2849,45 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array // 해당 개소의 BOM 기반 자재 추출 $materialItems = []; - if ($woItem->item_id) { + // ① dynamic_bom 우선 체크 (절곡 등 동적 BOM 사용 공정) + $options = is_string($woItem->options) ? json_decode($woItem->options, true) : ($woItem->options ?? []); + $dynamicBom = $options['dynamic_bom'] ?? null; + + if ($dynamicBom && is_array($dynamicBom)) { + // dynamic_bom child_item_id 배치 조회 (N+1 방지) + $childItemIds = array_filter(array_column($dynamicBom, 'child_item_id')); + $childItems = []; + if (! empty($childItemIds)) { + $childItems = \App\Models\Items\Item::where('tenant_id', $tenantId) + ->whereIn('id', array_unique($childItemIds)) + ->get() + ->keyBy('id'); + } + + foreach ($dynamicBom as $bomEntry) { + $childItemId = $bomEntry['child_item_id'] ?? null; + if (! $childItemId || ! isset($childItems[$childItemId])) { + continue; + } + + // dynamic_bom.qty는 아이템 수량이 곱해져 있으므로 나눠서 개소당 수량 산출 + // (작업일지 bendingInfo와 동일한 수량) + $bomQty = (float) ($bomEntry['qty'] ?? 1); + $woItemQty = max(1, (float) ($woItem->quantity ?? 1)); + $perNodeQty = $bomQty / $woItemQty; + $materialItems[] = [ + 'item' => $childItems[$childItemId], + 'bom_qty' => $perNodeQty, + 'required_qty' => $perNodeQty, + 'lot_prefix' => $bomEntry['lot_prefix'] ?? null, + 'part_type' => $bomEntry['part_type'] ?? null, + 'category' => $bomEntry['category'] ?? null, + ]; + } + } + + // ② dynamic_bom이 없으면 정적 BOM fallback + if (empty($materialItems) && $woItem->item_id) { $item = \App\Models\Items\Item::where('tenant_id', $tenantId) ->find($woItem->item_id); @@ -2936,6 +2976,9 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array 'receipt_date' => $lot->receipt_date, 'supplier' => $lot->supplier, 'fifo_rank' => $rank++, + 'lot_prefix' => $matInfo['lot_prefix'] ?? null, + 'part_type' => $matInfo['part_type'] ?? null, + 'category' => $matInfo['category'] ?? null, ]; } } @@ -2959,6 +3002,9 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array 'receipt_date' => null, 'supplier' => null, 'fifo_rank' => $rank++, + 'lot_prefix' => $matInfo['lot_prefix'] ?? null, + 'part_type' => $matInfo['part_type'] ?? null, + 'category' => $matInfo['category'] ?? null, ]; } }