feat:개소별 자재 투입 관리 API 추가

- work_order_material_inputs 테이블 신규 생성 (개소별 자재 투입 추적)
- 개소별 자재 조회/투입/이력/삭제/수정 API 5개 추가
- StockService.increaseToLot: LOT 수량 복원 메서드 추가
- WorkOrderService에 개소별 자재 투입 비즈니스 로직 구현
- WorkOrder, WorkOrderItem 모델에 materialInputs 관계 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 03:41:35 +09:00
parent d730c2d91a
commit e4c53c7b17
9 changed files with 872 additions and 70 deletions

View File

@@ -703,6 +703,90 @@ public function decreaseFromLot(int $stockLotId, float $qty, string $reason, int
});
}
/**
* 특정 LOT에 수량 복원 (투입 취소, 삭제 등)
* decreaseFromLot의 역방향
*/
public function increaseToLot(int $stockLotId, float $qty, string $reason, int $referenceId): array
{
$tenantId = $this->tenantId();
$userId = $this->apiUserId();
return DB::transaction(function () use ($stockLotId, $qty, $reason, $referenceId, $tenantId, $userId) {
$lot = StockLot::where('tenant_id', $tenantId)
->where('id', $stockLotId)
->lockForUpdate()
->first();
if (! $lot) {
throw new \Exception(__('error.stock.lot_not_available'));
}
$stock = Stock::where('id', $lot->stock_id)
->lockForUpdate()
->first();
if (! $stock) {
throw new \Exception(__('error.stock.not_found'));
}
$oldStockQty = $stock->stock_qty;
// LOT 수량 복원
$lot->qty += $qty;
$lot->available_qty += $qty;
$lot->updated_by = $userId;
if ($lot->status === 'used' && $lot->qty > 0) {
$lot->status = 'available';
}
$lot->save();
// Stock 정보 갱신
$stock->refreshFromLots();
// 거래 이력 기록
$this->recordTransaction(
stock: $stock,
type: StockTransaction::TYPE_IN,
qty: $qty,
reason: $reason,
referenceType: $reason,
referenceId: $referenceId,
lotNo: $lot->lot_no,
stockLotId: $lot->id
);
// 감사 로그
$this->logStockChange(
stock: $stock,
action: 'stock_increase',
reason: $reason,
referenceType: $reason,
referenceId: $referenceId,
qtyChange: $qty,
lotNo: $lot->lot_no
);
Log::info('Stock increased to specific lot', [
'stock_lot_id' => $stockLotId,
'lot_no' => $lot->lot_no,
'qty' => $qty,
'reason' => $reason,
'reference_id' => $referenceId,
'old_stock_qty' => $oldStockQty,
'new_stock_qty' => $stock->stock_qty,
]);
return [
'lot_id' => $lot->id,
'lot_no' => $lot->lot_no,
'restored_qty' => $qty,
'remaining_qty' => $lot->qty,
];
});
}
/**
* 품목별 가용 재고 조회
*