fix: [production] 자재투입 bom_group_key 개별 저장 — 동일 자재 다중 BOM 그룹 지원
- bom_group_key 컬럼 추가 마이그레이션 (work_order_material_inputs) - WorkOrderMaterialInput 모델 fillable에 bom_group_key 추가 - MaterialInputForItemRequest에 bom_group_key 검증 + replace 옵션 추가 - WorkOrderService.getMaterialsForItem: stock_lot_id+bom_group_key 복합키 기투입 조회 (하위호환) - WorkOrderService.registerMaterialInputForItem: bom_group_key 저장 + replace 모드 (기존 삭제→재등록) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3240,6 +3240,30 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array
|
||||
->groupBy('item_id')
|
||||
->pluck('total_qty', 'item_id');
|
||||
|
||||
// LOT별 기투입 수량 조회 (stock_lot_id + bom_group_key별 SUM)
|
||||
$lotInputtedRaw = WorkOrderMaterialInput::where('tenant_id', $tenantId)
|
||||
->where('work_order_id', $workOrderId)
|
||||
->where('work_order_item_id', $itemId)
|
||||
->whereNotNull('stock_lot_id')
|
||||
->selectRaw('stock_lot_id, bom_group_key, SUM(qty) as total_qty')
|
||||
->groupBy('stock_lot_id', 'bom_group_key')
|
||||
->get();
|
||||
|
||||
// bom_group_key 포함 복합키 매핑 + stock_lot_id 단순 매핑 (하위호환)
|
||||
$lotInputtedByGroup = [];
|
||||
$lotInputtedByLot = [];
|
||||
foreach ($lotInputtedRaw as $row) {
|
||||
$lotId = $row->stock_lot_id;
|
||||
$groupKey = $row->bom_group_key;
|
||||
$qty = (float) $row->total_qty;
|
||||
|
||||
if ($groupKey) {
|
||||
$compositeKey = $lotId.'_'.$groupKey;
|
||||
$lotInputtedByGroup[$compositeKey] = ($lotInputtedByGroup[$compositeKey] ?? 0) + $qty;
|
||||
}
|
||||
$lotInputtedByLot[$lotId] = ($lotInputtedByLot[$lotId] ?? 0) + $qty;
|
||||
}
|
||||
|
||||
// 자재별 LOT 조회
|
||||
$materials = [];
|
||||
$rank = 1;
|
||||
@@ -3283,6 +3307,7 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array
|
||||
'required_qty' => $matInfo['required_qty'],
|
||||
'already_inputted' => $alreadyInputted,
|
||||
'remaining_required_qty' => $remainingRequired,
|
||||
'lot_inputted_qty' => (float) ($lotInputtedByGroup[$lot->id.'_'.$bomGroupKey] ?? $lotInputtedByLot[$lot->id] ?? 0),
|
||||
'lot_qty' => (float) $lot->qty,
|
||||
'lot_available_qty' => (float) $lot->available_qty,
|
||||
'lot_reserved_qty' => (float) $lot->reserved_qty,
|
||||
@@ -3310,6 +3335,7 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array
|
||||
'required_qty' => $matInfo['required_qty'],
|
||||
'already_inputted' => $alreadyInputted,
|
||||
'remaining_required_qty' => $remainingRequired,
|
||||
'lot_inputted_qty' => 0,
|
||||
'lot_qty' => 0,
|
||||
'lot_available_qty' => 0,
|
||||
'lot_reserved_qty' => 0,
|
||||
@@ -3328,8 +3354,10 @@ public function getMaterialsForItem(int $workOrderId, int $itemId): array
|
||||
|
||||
/**
|
||||
* 개소별 자재 투입 등록
|
||||
*
|
||||
* @param bool $replace true면 기존 투입 이력을 삭제(재고 복원) 후 새로 등록
|
||||
*/
|
||||
public function registerMaterialInputForItem(int $workOrderId, int $itemId, array $inputs): array
|
||||
public function registerMaterialInputForItem(int $workOrderId, int $itemId, array $inputs, bool $replace = false): array
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
@@ -3347,13 +3375,32 @@ public function registerMaterialInputForItem(int $workOrderId, int $itemId, arra
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($inputs, $tenantId, $userId, $workOrderId, $itemId) {
|
||||
return DB::transaction(function () use ($inputs, $tenantId, $userId, $workOrderId, $itemId, $replace) {
|
||||
$stockService = app(StockService::class);
|
||||
$inputResults = [];
|
||||
|
||||
// replace 모드: 기존 투입 이력 삭제 + 재고 복원
|
||||
if ($replace) {
|
||||
$existingInputs = WorkOrderMaterialInput::where('tenant_id', $tenantId)
|
||||
->where('work_order_id', $workOrderId)
|
||||
->where('work_order_item_id', $itemId)
|
||||
->get();
|
||||
|
||||
foreach ($existingInputs as $existing) {
|
||||
$stockService->increaseToLot(
|
||||
stockLotId: $existing->stock_lot_id,
|
||||
qty: (float) $existing->qty,
|
||||
reason: 'work_order_input_replace',
|
||||
referenceId: $workOrderId
|
||||
);
|
||||
$existing->delete();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($inputs as $input) {
|
||||
$stockLotId = $input['stock_lot_id'] ?? null;
|
||||
$qty = (float) ($input['qty'] ?? 0);
|
||||
$bomGroupKey = $input['bom_group_key'] ?? null;
|
||||
|
||||
if (! $stockLotId || $qty <= 0) {
|
||||
continue;
|
||||
@@ -3378,6 +3425,7 @@ public function registerMaterialInputForItem(int $workOrderId, int $itemId, arra
|
||||
'work_order_item_id' => $itemId,
|
||||
'stock_lot_id' => $stockLotId,
|
||||
'item_id' => $lotItemId ?? 0,
|
||||
'bom_group_key' => $bomGroupKey,
|
||||
'qty' => $qty,
|
||||
'input_by' => $userId,
|
||||
'input_at' => now(),
|
||||
|
||||
Reference in New Issue
Block a user