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:
@@ -17,6 +17,8 @@ public function rules(): array
|
||||
'inputs' => 'required|array|min:1',
|
||||
'inputs.*.stock_lot_id' => 'required|integer',
|
||||
'inputs.*.qty' => 'required|numeric|gt:0',
|
||||
'inputs.*.bom_group_key' => 'sometimes|nullable|string|max:100',
|
||||
'replace' => 'sometimes|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class WorkOrderMaterialInput extends Model
|
||||
'work_order_item_id',
|
||||
'stock_lot_id',
|
||||
'item_id',
|
||||
'bom_group_key',
|
||||
'qty',
|
||||
'input_by',
|
||||
'input_at',
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('work_order_material_inputs', function (Blueprint $table) {
|
||||
$table->string('bom_group_key')->nullable()->after('item_id')
|
||||
->comment('BOM 그룹키 (같은 item_id의 다른 용도 구분, ex: itemId_category_partType)');
|
||||
|
||||
$table->index(['work_order_item_id', 'bom_group_key'], 'idx_womi_item_bomgroup');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('work_order_material_inputs', function (Blueprint $table) {
|
||||
$table->dropIndex('idx_womi_item_bomgroup');
|
||||
$table->dropColumn('bom_group_key');
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user