feat(WEB): 절곡품 선생산→재고적재 Phase 1 - 생산입고 기반 구축
- StockTransaction: REASON_PRODUCTION_OUTPUT 상수 및 '생산입고' 라벨 추가 - StockLot: work_order_id FK 컬럼 마이그레이션 + 모델 fillable/casts/relation 추가 - StockService: increaseFromProduction() 메서드 구현 (increaseFromReceiving 기반) - WorkOrderService: 완료 시 sales_order_id 유무에 따라 출하/재고입고 분기 - stockInFromProduction(): 품목별 양품 재고 입고 처리 - shouldStockIn(): items.options 기반 입고 대상 판단 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Items\Item;
|
||||
use App\Models\Production\WorkOrder;
|
||||
use App\Models\Production\WorkOrderItem;
|
||||
use App\Models\Tenants\Receiving;
|
||||
use App\Models\Tenants\Stock;
|
||||
use App\Models\Tenants\StockLot;
|
||||
@@ -313,6 +315,99 @@ public function increaseFromReceiving(Receiving $receiving): StockLot
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산 완료 시 완성품 재고 입고
|
||||
*
|
||||
* increaseFromReceiving()을 기반으로 구현.
|
||||
* 선생산(수주 없는 작업지시) 완료 시 양품을 재고로 적재.
|
||||
*
|
||||
* @param WorkOrder $workOrder 선생산 작업지시
|
||||
* @param WorkOrderItem $woItem 작업지시 품목
|
||||
* @param float $goodQty 양품 수량
|
||||
* @param string $lotNo LOT 번호
|
||||
* @return StockLot 생성된 StockLot
|
||||
*/
|
||||
public function increaseFromProduction(
|
||||
WorkOrder $workOrder,
|
||||
WorkOrderItem $woItem,
|
||||
float $goodQty,
|
||||
string $lotNo
|
||||
): StockLot {
|
||||
if (! $woItem->item_id) {
|
||||
throw new \Exception(__('error.stock.item_id_required'));
|
||||
}
|
||||
|
||||
$tenantId = $this->tenantId();
|
||||
$userId = $this->apiUserId();
|
||||
|
||||
return DB::transaction(function () use ($workOrder, $woItem, $goodQty, $lotNo, $tenantId, $userId) {
|
||||
// 1. Stock 조회 또는 생성
|
||||
$stock = $this->getOrCreateStock($woItem->item_id);
|
||||
|
||||
// 2. FIFO 순서 계산
|
||||
$fifoOrder = $this->getNextFifoOrder($stock->id);
|
||||
|
||||
// 3. StockLot 생성
|
||||
$stockLot = new StockLot;
|
||||
$stockLot->tenant_id = $tenantId;
|
||||
$stockLot->stock_id = $stock->id;
|
||||
$stockLot->lot_no = $lotNo;
|
||||
$stockLot->fifo_order = $fifoOrder;
|
||||
$stockLot->receipt_date = now()->toDateString();
|
||||
$stockLot->qty = $goodQty;
|
||||
$stockLot->reserved_qty = 0;
|
||||
$stockLot->available_qty = $goodQty;
|
||||
$stockLot->unit = $woItem->unit ?? 'EA';
|
||||
$stockLot->supplier = null;
|
||||
$stockLot->supplier_lot = null;
|
||||
$stockLot->po_number = null;
|
||||
$stockLot->location = null;
|
||||
$stockLot->status = 'available';
|
||||
$stockLot->receiving_id = null;
|
||||
$stockLot->work_order_id = $workOrder->id;
|
||||
$stockLot->created_by = $userId;
|
||||
$stockLot->updated_by = $userId;
|
||||
$stockLot->save();
|
||||
|
||||
// 4. Stock 합계 갱신
|
||||
$stock->refreshFromLots();
|
||||
|
||||
// 5. 거래 이력 기록
|
||||
$this->recordTransaction(
|
||||
stock: $stock,
|
||||
type: StockTransaction::TYPE_IN,
|
||||
qty: $goodQty,
|
||||
reason: StockTransaction::REASON_PRODUCTION_OUTPUT,
|
||||
referenceType: 'work_order',
|
||||
referenceId: $workOrder->id,
|
||||
lotNo: $lotNo,
|
||||
stockLotId: $stockLot->id
|
||||
);
|
||||
|
||||
// 6. 감사 로그 기록
|
||||
$this->logStockChange(
|
||||
stock: $stock,
|
||||
action: 'production_in',
|
||||
reason: 'production_output',
|
||||
referenceType: 'work_order',
|
||||
referenceId: $workOrder->id,
|
||||
qtyChange: $goodQty,
|
||||
lotNo: $lotNo
|
||||
);
|
||||
|
||||
Log::info('Stock increased from production', [
|
||||
'work_order_id' => $workOrder->id,
|
||||
'item_id' => $woItem->item_id,
|
||||
'stock_id' => $stock->id,
|
||||
'stock_lot_id' => $stockLot->id,
|
||||
'qty' => $goodQty,
|
||||
'lot_no' => $lotNo,
|
||||
]);
|
||||
|
||||
return $stockLot;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 입고 수정 시 재고 조정 (차이만큼 증감)
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user