feat: [재공품] STOCK 작업지시 dynamic_bom 생성 + 자재 매칭 구현
- BendingCodeService: lengthCodeToMm() public static 메서드 추가 - BendingInfoBuilder: buildDynamicBomForStockItem() 신규 메서드 - bending_lot(prodCode/specCode/lengthCode) + partKey → BD 품목 코드 → dynamic_bom 엔트리 생성 - PrefixResolver 활용하여 정확한 prefix 매핑 - OrderService: STOCK 확정 시 bending_lot 기반 dynamic_bom 자동 생성 - WorkOrderService: 기존 STOCK 호환 fallback (getMaterials + getMaterialsForItem) - dynamic_bom 없는 기존 재공품도 bending_lot.material로 원자재 검색
This commit is contained in:
@@ -1782,6 +1782,40 @@ public function getMaterials(int $workOrderId): array
|
||||
];
|
||||
}
|
||||
|
||||
// STOCK 호환: item_id 없는 기존 재공품 → bending_lot.material 기반 원자재 검색
|
||||
if (empty($materialItems) && ! $woItem->item_id) {
|
||||
$woBendingInfo = $workOrder->options['bending_info'] ?? [];
|
||||
if (! empty($woBendingInfo['isStockProduction'])) {
|
||||
$salesOrder = $workOrder->salesOrder ?? \App\Models\Orders\Order::find($workOrder->sales_order_id);
|
||||
$bendingLot = $salesOrder?->options['bending_lot'] ?? null;
|
||||
$material = $bendingLot['material'] ?? null;
|
||||
$lengthCode = $bendingLot['length_code'] ?? null;
|
||||
$prodCode = $bendingLot['prod_code'] ?? '';
|
||||
|
||||
if ($material && $lengthCode) {
|
||||
$lengthMm = \App\Services\BendingCodeService::lengthCodeToMm($prodCode, $lengthCode);
|
||||
if (preg_match('/^([A-Za-zㄱ-ㅎ가-힣]+)\s*(\d+\.?\d*)/u', $material, $matMatch)) {
|
||||
$matName = $matMatch[1];
|
||||
$matThickness = (float) $matMatch[2];
|
||||
$rawItems = \App\Models\Items\Item::where('tenant_id', $tenantId)
|
||||
->where('item_type', 'RM')
|
||||
->where('name', 'LIKE', "%{$matName}{$matThickness}%")
|
||||
->get();
|
||||
foreach ($rawItems as $rawItem) {
|
||||
if ($lengthMm > 0 && ! str_contains($rawItem->name, (string) $lengthMm)) {
|
||||
continue;
|
||||
}
|
||||
$materialItems[] = [
|
||||
'item' => $rawItem,
|
||||
'bom_qty' => 1,
|
||||
'required_qty' => $woItem->quantity ?? 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 기존 방식: item_id 기준 합산
|
||||
foreach ($materialItems as $matInfo) {
|
||||
$itemId = $matInfo['item']->id;
|
||||
@@ -4072,6 +4106,46 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array
|
||||
}
|
||||
}
|
||||
|
||||
// ④ STOCK 호환: dynamic_bom/BOM/item 모두 없는 기존 재공품 → bending_lot.material 기반 원자재 검색
|
||||
if (empty($materialItems) && ! $woItem->item_id) {
|
||||
$woBendingInfo = $workOrder->options['bending_info'] ?? [];
|
||||
if (! empty($woBendingInfo['isStockProduction'])) {
|
||||
$salesOrder = \App\Models\Orders\Order::find($workOrder->sales_order_id);
|
||||
$bendingLot = $salesOrder?->options['bending_lot'] ?? null;
|
||||
$material = $bendingLot['material'] ?? null;
|
||||
$lengthCode = $bendingLot['length_code'] ?? null;
|
||||
$prodCode = $bendingLot['prod_code'] ?? '';
|
||||
|
||||
if ($material && $lengthCode) {
|
||||
$lengthMm = \App\Services\BendingCodeService::lengthCodeToMm($prodCode, $lengthCode);
|
||||
|
||||
// material "SUS 1.2T" 또는 "EGI 1.55T" → 재질명 + 두께 파싱
|
||||
if (preg_match('/^([A-Za-zㄱ-ㅎ가-힣]+)\s*(\d+\.?\d*)/u', $material, $matMatch)) {
|
||||
$matName = $matMatch[1];
|
||||
$matThickness = (float) $matMatch[2];
|
||||
|
||||
// items 테이블에서 RM(원자재) 검색: 이름에 재질명 + 두께 포함
|
||||
$rawItems = \App\Models\Items\Item::where('tenant_id', $tenantId)
|
||||
->where('item_type', 'RM')
|
||||
->where('name', 'LIKE', "%{$matName}{$matThickness}%")
|
||||
->get();
|
||||
|
||||
// 길이 조건: 원자재 이름에 길이(mm) 포함 여부
|
||||
foreach ($rawItems as $rawItem) {
|
||||
if ($lengthMm > 0 && ! str_contains($rawItem->name, (string) $lengthMm)) {
|
||||
continue;
|
||||
}
|
||||
$materialItems[] = [
|
||||
'item' => $rawItem,
|
||||
'bom_qty' => 1,
|
||||
'required_qty' => $woItem->quantity ?? 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 이미 투입된 수량 조회 (item_id별 SUM)
|
||||
$inputtedQties = WorkOrderMaterialInput::where('tenant_id', $tenantId)
|
||||
->where('work_order_id', $workOrderId)
|
||||
|
||||
Reference in New Issue
Block a user