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 기반 + 로트별 재고)
|
||||||
*
|
*
|
||||||
* 작업지시 품목의 BOM 자재별로 StockLot(입고 로트)를 FIFO 순서로 반환합니다.
|
* 작업지시 품목의 BOM 자재별로 StockLot(입고 로트)를 FIFO 순서로 반환합니다.
|
||||||
* 로트번호는 입고관리(Receiving)에서 생성된 실제 로트번호입니다.
|
* 동일 자재가 여러 작업지시 품목에 걸쳐 있으면 필요수량을 합산하고 로트는 중복 없이 반환합니다.
|
||||||
*
|
*
|
||||||
* @param int $workOrderId 작업지시 ID
|
* @param int $workOrderId 작업지시 ID
|
||||||
* @return array 자재 목록 (로트 단위)
|
* @return array 자재 목록 (로트 단위)
|
||||||
@@ -1109,8 +1109,8 @@ public function getMaterials(int $workOrderId): array
|
|||||||
throw new NotFoundHttpException(__('error.not_found'));
|
throw new NotFoundHttpException(__('error.not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$materials = [];
|
// Phase 1: 작업지시 품목들에서 유니크 자재 목록 수집 (item_id 기준 합산)
|
||||||
$rank = 1;
|
$uniqueMaterials = [];
|
||||||
|
|
||||||
foreach ($workOrder->items as $woItem) {
|
foreach ($workOrder->items as $woItem) {
|
||||||
$materialItems = [];
|
$materialItems = [];
|
||||||
@@ -1140,7 +1140,6 @@ public function getMaterials(int $workOrderId): array
|
|||||||
'item' => $childItem,
|
'item' => $childItem,
|
||||||
'bom_qty' => $bomQty,
|
'bom_qty' => $bomQty,
|
||||||
'required_qty' => $bomQty * ($woItem->quantity ?? 1),
|
'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,
|
'item' => $woItem->item,
|
||||||
'bom_qty' => 1,
|
'bom_qty' => 1,
|
||||||
'required_qty' => $woItem->quantity ?? 1,
|
'required_qty' => $woItem->quantity ?? 1,
|
||||||
'work_order_item_id' => $woItem->id,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 각 자재별로 StockLot(입고 로트) 조회
|
// 유니크 자재 수집 (같은 item_id면 required_qty 합산)
|
||||||
foreach ($materialItems as $matInfo) {
|
foreach ($materialItems as $matInfo) {
|
||||||
$materialItem = $matInfo['item'];
|
$itemId = $matInfo['item']->id;
|
||||||
|
if (isset($uniqueMaterials[$itemId])) {
|
||||||
// Stock 조회
|
$uniqueMaterials[$itemId]['required_qty'] += $matInfo['required_qty'];
|
||||||
$stock = \App\Models\Tenants\Stock::where('tenant_id', $tenantId)
|
} else {
|
||||||
->where('item_id', $materialItem->id)
|
$uniqueMaterials[$itemId] = $matInfo;
|
||||||
->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++,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 가용 로트가 없는 경우 자재 정보만 반환 (재고 없음 표시)
|
// Phase 2: 유니크 자재별로 StockLot 조회
|
||||||
$hasLots = collect($materials)->where('item_id', $materialItem->id)->isNotEmpty();
|
$materials = [];
|
||||||
if (! $hasLots) {
|
$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[] = [
|
$materials[] = [
|
||||||
'stock_lot_id' => null,
|
'stock_lot_id' => $lot->id,
|
||||||
'item_id' => $materialItem->id,
|
'item_id' => $materialItem->id,
|
||||||
'work_order_item_id' => $matInfo['work_order_item_id'],
|
'lot_no' => $lot->lot_no,
|
||||||
'lot_no' => null,
|
|
||||||
'material_code' => $materialItem->code,
|
'material_code' => $materialItem->code,
|
||||||
'material_name' => $materialItem->name,
|
'material_name' => $materialItem->name,
|
||||||
'specification' => $materialItem->specification,
|
'specification' => $materialItem->specification,
|
||||||
'unit' => $materialItem->unit ?? 'EA',
|
'unit' => $lot->unit ?? $materialItem->unit ?? 'EA',
|
||||||
'bom_qty' => $matInfo['bom_qty'],
|
'bom_qty' => $matInfo['bom_qty'],
|
||||||
'required_qty' => $matInfo['required_qty'],
|
'required_qty' => $matInfo['required_qty'],
|
||||||
'lot_qty' => 0,
|
'lot_qty' => (float) $lot->qty,
|
||||||
'lot_available_qty' => 0,
|
'lot_available_qty' => (float) $lot->available_qty,
|
||||||
'lot_reserved_qty' => 0,
|
'lot_reserved_qty' => (float) $lot->reserved_qty,
|
||||||
'receipt_date' => null,
|
'receipt_date' => $lot->receipt_date,
|
||||||
'supplier' => null,
|
'supplier' => $lot->supplier,
|
||||||
'fifo_rank' => $rank++,
|
'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;
|
return $materials;
|
||||||
|
|||||||
Reference in New Issue
Block a user