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, ]; } }