fix(API): 자재투입 모달 중복 로트 버그 수정
동일 자재가 여러 작업지시 품목에 걸쳐 있을 때 StockLot이 중복 표시되던 문제 수정. Phase 1(유니크 자재 수집) → Phase 2(로트 조회) 구조로 변경하여 중복 제거 및 필요수량 합산. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1092,7 +1092,7 @@ public function updateItemStatus(int $workOrderId, int $itemId, string $status)
|
||||
* 작업지시에 필요한 자재 목록 조회 (BOM 기반 + 로트별 재고)
|
||||
*
|
||||
* 작업지시 품목의 BOM 자재별로 StockLot(입고 로트)를 FIFO 순서로 반환합니다.
|
||||
* 로트번호는 입고관리(Receiving)에서 생성된 실제 로트번호입니다.
|
||||
* 동일 자재가 여러 작업지시 품목에 걸쳐 있으면 필요수량을 합산하고 로트는 중복 없이 반환합니다.
|
||||
*
|
||||
* @param int $workOrderId 작업지시 ID
|
||||
* @return array 자재 목록 (로트 단위)
|
||||
@@ -1109,8 +1109,8 @@ public function getMaterials(int $workOrderId): array
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
$materials = [];
|
||||
$rank = 1;
|
||||
// Phase 1: 작업지시 품목들에서 유니크 자재 목록 수집 (item_id 기준 합산)
|
||||
$uniqueMaterials = [];
|
||||
|
||||
foreach ($workOrder->items as $woItem) {
|
||||
$materialItems = [];
|
||||
@@ -1140,7 +1140,6 @@ public function getMaterials(int $workOrderId): array
|
||||
'item' => $childItem,
|
||||
'bom_qty' => $bomQty,
|
||||
'required_qty' => $bomQty * ($woItem->quantity ?? 1),
|
||||
'work_order_item_id' => $woItem->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1152,73 +1151,83 @@ public function getMaterials(int $workOrderId): array
|
||||
'item' => $woItem->item,
|
||||
'bom_qty' => 1,
|
||||
'required_qty' => $woItem->quantity ?? 1,
|
||||
'work_order_item_id' => $woItem->id,
|
||||
];
|
||||
}
|
||||
|
||||
// 각 자재별로 StockLot(입고 로트) 조회
|
||||
// 유니크 자재 수집 (같은 item_id면 required_qty 합산)
|
||||
foreach ($materialItems as $matInfo) {
|
||||
$materialItem = $matInfo['item'];
|
||||
|
||||
// Stock 조회
|
||||
$stock = \App\Models\Tenants\Stock::where('tenant_id', $tenantId)
|
||||
->where('item_id', $materialItem->id)
|
||||
->first();
|
||||
|
||||
if ($stock) {
|
||||
// 가용 로트를 FIFO 순서로 조회
|
||||
$lots = \App\Models\Tenants\StockLot::where('tenant_id', $tenantId)
|
||||
->where('stock_id', $stock->id)
|
||||
->where('status', 'available')
|
||||
->where('available_qty', '>', 0)
|
||||
->orderBy('fifo_order', 'asc')
|
||||
->get();
|
||||
|
||||
foreach ($lots as $lot) {
|
||||
$materials[] = [
|
||||
'stock_lot_id' => $lot->id,
|
||||
'item_id' => $materialItem->id,
|
||||
'work_order_item_id' => $matInfo['work_order_item_id'],
|
||||
'lot_no' => $lot->lot_no,
|
||||
'material_code' => $materialItem->code,
|
||||
'material_name' => $materialItem->name,
|
||||
'specification' => $materialItem->specification,
|
||||
'unit' => $lot->unit ?? $materialItem->unit ?? 'EA',
|
||||
'bom_qty' => $matInfo['bom_qty'],
|
||||
'required_qty' => $matInfo['required_qty'],
|
||||
'lot_qty' => (float) $lot->qty,
|
||||
'lot_available_qty' => (float) $lot->available_qty,
|
||||
'lot_reserved_qty' => (float) $lot->reserved_qty,
|
||||
'receipt_date' => $lot->receipt_date,
|
||||
'supplier' => $lot->supplier,
|
||||
'fifo_rank' => $rank++,
|
||||
];
|
||||
}
|
||||
$itemId = $matInfo['item']->id;
|
||||
if (isset($uniqueMaterials[$itemId])) {
|
||||
$uniqueMaterials[$itemId]['required_qty'] += $matInfo['required_qty'];
|
||||
} else {
|
||||
$uniqueMaterials[$itemId] = $matInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 가용 로트가 없는 경우 자재 정보만 반환 (재고 없음 표시)
|
||||
$hasLots = collect($materials)->where('item_id', $materialItem->id)->isNotEmpty();
|
||||
if (! $hasLots) {
|
||||
// Phase 2: 유니크 자재별로 StockLot 조회
|
||||
$materials = [];
|
||||
$rank = 1;
|
||||
|
||||
foreach ($uniqueMaterials as $matInfo) {
|
||||
$materialItem = $matInfo['item'];
|
||||
|
||||
$stock = \App\Models\Tenants\Stock::where('tenant_id', $tenantId)
|
||||
->where('item_id', $materialItem->id)
|
||||
->first();
|
||||
|
||||
$lotsFound = false;
|
||||
|
||||
if ($stock) {
|
||||
$lots = \App\Models\Tenants\StockLot::where('tenant_id', $tenantId)
|
||||
->where('stock_id', $stock->id)
|
||||
->where('status', 'available')
|
||||
->where('available_qty', '>', 0)
|
||||
->orderBy('fifo_order', 'asc')
|
||||
->get();
|
||||
|
||||
foreach ($lots as $lot) {
|
||||
$lotsFound = true;
|
||||
$materials[] = [
|
||||
'stock_lot_id' => null,
|
||||
'stock_lot_id' => $lot->id,
|
||||
'item_id' => $materialItem->id,
|
||||
'work_order_item_id' => $matInfo['work_order_item_id'],
|
||||
'lot_no' => null,
|
||||
'lot_no' => $lot->lot_no,
|
||||
'material_code' => $materialItem->code,
|
||||
'material_name' => $materialItem->name,
|
||||
'specification' => $materialItem->specification,
|
||||
'unit' => $materialItem->unit ?? 'EA',
|
||||
'unit' => $lot->unit ?? $materialItem->unit ?? 'EA',
|
||||
'bom_qty' => $matInfo['bom_qty'],
|
||||
'required_qty' => $matInfo['required_qty'],
|
||||
'lot_qty' => 0,
|
||||
'lot_available_qty' => 0,
|
||||
'lot_reserved_qty' => 0,
|
||||
'receipt_date' => null,
|
||||
'supplier' => null,
|
||||
'lot_qty' => (float) $lot->qty,
|
||||
'lot_available_qty' => (float) $lot->available_qty,
|
||||
'lot_reserved_qty' => (float) $lot->reserved_qty,
|
||||
'receipt_date' => $lot->receipt_date,
|
||||
'supplier' => $lot->supplier,
|
||||
'fifo_rank' => $rank++,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 가용 로트가 없는 경우 자재 정보만 반환 (재고 없음 표시)
|
||||
if (! $lotsFound) {
|
||||
$materials[] = [
|
||||
'stock_lot_id' => null,
|
||||
'item_id' => $materialItem->id,
|
||||
'lot_no' => null,
|
||||
'material_code' => $materialItem->code,
|
||||
'material_name' => $materialItem->name,
|
||||
'specification' => $materialItem->specification,
|
||||
'unit' => $materialItem->unit ?? 'EA',
|
||||
'bom_qty' => $matInfo['bom_qty'],
|
||||
'required_qty' => $matInfo['required_qty'],
|
||||
'lot_qty' => 0,
|
||||
'lot_available_qty' => 0,
|
||||
'lot_reserved_qty' => 0,
|
||||
'receipt_date' => null,
|
||||
'supplier' => null,
|
||||
'fifo_rank' => $rank++,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $materials;
|
||||
|
||||
Reference in New Issue
Block a user